skip to Main Content

I have a MainPage, where I have a ListView of my rooms. The ListView uses the response from my getUserRooms service. Everything works fine; I can’t understand why The ListView doesn’t update when my createRoom request gets called, which is in the same service as the getUserRooms functionality. I’m managing the state using Riverpod

Here are my HTTP requests:

class RoomService {
  RoomRoutes roomRoutes = RoomRoutes();

  final _preferencesService = SharedPreferences();

  Future<List> createRoom(RoomModelRequest postBody) async {
    if (postBody.roomPassword!.trim().isEmpty) {
      postBody.roomPassword = null;
    }

    if (postBody.roomName!.trim().isEmpty) {
      postBody.roomName = null;
    }

    try {
      final userData = await _preferencesService.getPreferences();

      final response = await http.post(Uri.parse(roomRoutes.roomsURL),
          headers: {
            'Content-Type': 'application/json; charset=UTF-8',
            'Authorization': 'Token ${userData.token}'
          },
          body: jsonEncode(postBody.toJson()));

      switch (response.statusCode) {
        case 201:
          final data = jsonDecode(response.body);
          return [response.statusCode, data["Success"]];

        
        default:
          throw Exception(response.reasonPhrase);
      }
    } on SocketException {
      throw Exception("No internet connection");
    } on TimeoutException catch (e) {
      throw Exception("Connection timeout: ${e.message} ");
    } on Exception {
      rethrow;
    }
  }

  Future<List<RoomModelResponse>> getUserRooms() async {
    try {
      final userData = await _preferencesService.getPreferences();

      final response =
          await http.get(Uri.parse(roomRoutes.extendedRoomsURL), headers: {
        'Content-Type': 'application/json; charset=UTF-8',
        'Authorization': 'Token ${userData.token}'
      });

      switch (response.statusCode) {
        case 200:
          Iterable json = jsonDecode(response.body);
          return json.map((room) => RoomModelResponse.fromJson(room)).toList();

       
        default:
          throw Exception(response.reasonPhrase);
      }
    } on SocketException {
      throw Exception("No internet connection");
    } on TimeoutException catch (e) {
      throw Exception("Connection timeout: ${e.message} ");
    } on Exception {
      rethrow;
    }
  }
}

Here in the same file, I have my room provider:

final roomProvider = Provider<RoomService>((ref) => RoomService());

And I’m watching the state of that provider, where on update the getUserRooms is returned.

final roomDataProvider = FutureProvider<List<RoomModelResponse>>((ref) async {
  return ref.watch(roomProvider).getUserRooms();
});

In my MainPage that is how I get the response data

class MainPage extends ConsumerWidget {
  const MainPage({super.key});

  @override
  Widget build(BuildContext context, ref) {
    // provider
    final data = ref.watch(roomDataProvider);

    final roomIDController = TextEditingController();
    final uniqueIDRoom = TextEditingController();

    // UI screen size
    final size = MediaQuery.of(context).size;

    double deviceWidth = size.width;
    double deviceHeight = size.height;

    return Scaffold(
        backgroundColor: bluePrimary,
        body: data.when(
            data: (data) {
              List<RoomModelResponse> roomList =
                  data.map((room) => room).toList();
              return SafeArea(
                  child: Container(
                padding:
                    const EdgeInsets.symmetric(horizontal: 36, vertical: 16),
                child: Column(
                    //crossAxisAlignment: CrossAxisAlignment.center,
                    children: [
                      Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          IconButton(
                            iconSize: deviceWidth * 0.09,
                            icon: const Icon(Icons.person_outline,
                                color: orangePrimary),
                            onPressed: () {},
                          ),
                          IconButton(
                            icon: const Icon(
                              Icons.add,
                              color: orangePrimary,
                            ),
                            iconSize: deviceWidth * 0.09,
                            onPressed: () {
                              Navigator.push(
                                context,
                                MaterialPageRoute(
                                    builder: (context) =>
                                        const CreateRoomScreen()),
                              );
                            },
                          )
                        ],
                      ),
                      SizedBox(height: deviceHeight * 0.04),
                      Align(
                        alignment: Alignment.centerLeft,
                        child: Text("W",
                            style: TextStyle(
                                fontFamily: 'Chalet',
                                fontSize: deviceWidth * 0.12,
                                color: orangePrimary,
                                fontWeight: FontWeight.w300,
                                height: deviceHeight * 0.001)),
                      ),
                      Align(
                        alignment: Alignment.centerLeft,
                        child: Text("W",
                            style: TextStyle(
                                fontFamily: 'Chalet',
                                fontSize: deviceWidth * 0.12,
                                color: whitePrimary,
                                fontWeight: FontWeight.w300,
                                height: deviceHeight * 0.001)),
                      ),
                      Align(
                        alignment: Alignment.centerLeft,
                        child: Text("M",
                            style: TextStyle(
                                fontFamily: 'Chalet',
                                fontSize: deviceWidth * 0.12,
                                color: orangePrimary,
                                fontWeight: FontWeight.w300,
                                height: deviceHeight * 0.001)),
                      ),
                      SizedBox(height: deviceHeight * 0.04),
                      Align(
                        alignment: Alignment.centerLeft,
                        child: Text("Join room",
                            style: TextStyle(
                                fontFamily: 'Chalet',
                                fontSize: deviceWidth * 0.07,
                                color: whitePrimary,
                                fontWeight: FontWeight.w100,
                                height: deviceHeight * 0.001)),
                      ),
                      SizedBox(height: deviceHeight * 0.008),

                      // email textField
                      SizedBox(
                        width: MediaQuery.of(context).size.width * 0.85,
                        child: TextField(
                          controller: roomIDController,
                          decoration: InputDecoration(
                              filled: true,
                              fillColor: whitePrimary,
                              border: OutlineInputBorder(
                                  borderRadius: BorderRadius.circular(12),
                                  borderSide: BorderSide.none),
                              hintText: 'Enter room ID to join it',
                              hintStyle: const TextStyle(
                                  color: Color.fromARGB(255, 174, 173, 173))),
                        ),
                      ),

                      SizedBox(height: deviceHeight * 0.016),

                      Align(
                        alignment: Alignment.bottomRight,
                        child: FloatingActionButton(
                          backgroundColor: orangePrimary,
                          child: const Icon(Icons.arrow_forward_ios_rounded,
                              color: whitePrimary),
                          onPressed: () {},
                        ),
                      ),

                      SizedBox(height: deviceHeight * 0.020),

                      Align(
                        alignment: Alignment.centerLeft,
                        child: Text("My rooms",
                            style: TextStyle(
                                fontFamily: 'Chalet',
                                fontSize: deviceWidth * 0.07,
                                color: whitePrimary,
                                fontWeight: FontWeight.w100,
                                height: deviceHeight * 0.001)),
                      ),

                      SizedBox(height: deviceHeight * 0.014),

                      // Display horizontal scroll rooms
                      Align(
                        alignment: Alignment.centerLeft,
                        child: SizedBox(
                          width: deviceWidth,
                          child: SizedBox(
                            height: deviceWidth * 0.56,
                            width: deviceWidth * 0.42,
                            child: ListView.builder(
                              scrollDirection: Axis.horizontal,
                              itemCount: roomList.length,
                              itemBuilder: (context, index) {
                                return Stack(children: [
                                  Container(
                                    height: deviceWidth * 0.8,
                                    width: deviceWidth * 0.42,
                                    margin: const EdgeInsets.symmetric(
                                        horizontal: 3),
                                    decoration: BoxDecoration(
                                        color: Colors.white,
                                        borderRadius:
                                            BorderRadius.circular(10)),
                                  ),
                                  InkWell(
                                    child: Container(
                                      height: deviceWidth * 0.4,
                                      width: deviceWidth * 0.42,
                                      margin: const EdgeInsets.symmetric(
                                          horizontal: 3),
                                      decoration: BoxDecoration(
                                          color: orangePrimary,
                                          borderRadius:
                                              BorderRadius.circular(10),
                                          boxShadow: const [
                                            BoxShadow(
                                                color: Colors.black,
                                                offset: Offset(0, 5),
                                                blurRadius: 10)
                                          ]),
                                      child: Image.asset(
                                          "assets/Logo.png"),
                                    ),
                                    onTap: () {},
                                  ),
                                  Positioned(
                                      bottom: 35,
                                      left: 12,
                                      child: Text(roomList[index].roomName)),
                                  Positioned(
                                      bottom: 15,
                                      left: 12,
                                      child: GestureDetector(
                                        onTap: () {
                                          uniqueIDRoom.text =
                                              roomList[index].uniqueID;
                                          Clipboard.setData(ClipboardData(
                                              text: uniqueIDRoom.text));

                                          const snackBar = SnackBar(
                                            content: Text("Copied room ID"),
                                          );

                                          // Find the ScaffoldMessenger in the widget tree
                                          // and use it to show a SnackBar.
                                          ScaffoldMessenger.of(context)
                                              .showSnackBar(snackBar);
                                        },
                                        child: Text(
                                            "ID: ${roomList[index].uniqueID}",
                                            style: const TextStyle(
                                                fontWeight: FontWeight.bold)),
                                      ))
                                ]);
                              },
                            ),
                          ),
                        ),
                      )
                    ]),
              ));
            },
            error: (err, s) => Text(err.toString()),
            loading: () => const Center(
                  child: CircularProgressIndicator(),
                )));
  }
}
 

Then, to create the room, I navigate the user to another screen perform a HTTP request, which, if successful returns the user to the MainPage again, and I expect the rooms to be updated, including the new room, also.

2

Answers


    1. Ensure that the state is being updated:

      ref.read(roomDataProvider.notifier).refresh();

    2. Use StateNotifierProvider instead of FutureProvider for better control:

      final roomStateProvider = StateNotifierProvider<RoomNotifier, List>((ref) {
      return RoomNotifier(ref.read);
      });

          class RoomNotifier extends StateNotifier<List<RoomModelResponse>> {
            final Reader read;
      
            RoomNotifier(this.read) : super([]);
      
            Future<void> refresh() async {
              final rooms = await read(roomProvider).getUserRooms();
              state = rooms;
            }
          }
      
    3. Use Consumer widget to watch for changes:

      Consumer(
      builder: (context, ref, child) {
      final rooms = ref.watch(roomStateProvider);
      // Build UI using rooms
      },
      )

    4. Check if the roomDataProvider is being called on screen re-entry:

      // In your MainPage build method
      final data = ref.watch(roomDataProvider);
      if (data.isEmpty) {
      // Fetch data again or trigger a refresh
      ref.refresh(roomDataProvider);
      }

    Login or Signup to reply.
  1. Your RoomService is not implemented to notify changes to listeners of the provider. You shall modify you code such that RoomService extends a Notifier class, in your case it should be AsyncNotifier.

    It is key that you identify and clearly define what the state returned by your provider is, and implement your code accordingly (maybe the list of rooms in your case??)

    Then when that state is modified, for example when getting data from the createRoom or getUserRooms you shall assign that new data object to the state variable of the Riverpod notifier, which will notify any Consumer widgets which are "watching" the roomServiceProvider.

    Your use case is almost exactly what the official Riverpod documentation explains in its example for To-do lists. You should take a look at it.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search