Provider not rebuilding on flutter

Issue

suddently from nowhere i came up with provider not re rendering my home page when it’s updated. I’ve inspected it and it IS UPDATED. It has newer data when i change it in firebase but the UI won’t re-render showing the new data. That’s my code:

Main function

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:my_event_app/pages/HomePage/home_page.dart';
import 'package:my_event_app/pages/Login/login_page.dart';
import 'package:my_event_app/providers/User/user.dart';
import 'package:provider/provider.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => UserModel()),
      ],
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        title: 'Flutter Demo',
        theme: ThemeData(
          // is not restarted.
          primarySwatch: Colors.blue,
        ),
        home: const Wrapper(),
      ),
    );
  }
}

class Wrapper extends StatelessWidget {
  const Wrapper({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
      stream: FirebaseAuth.instance.userChanges(),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.active) {
          User? user = snapshot.data;
          if (user == null) {
            return const LoginPage();
          }
          return StreamBuilder<DocumentSnapshot>(
              stream: FirebaseFirestore.instance
                  .collection('users')
                  .doc(FirebaseAuth.instance.currentUser!.uid)
                  .snapshots(),
              builder: (context, userSnapshot) {
                if (userSnapshot.hasData) {
                  Provider.of<UserModel>(context, listen: true)
                      .fromJson(userSnapshot.data!.data());
                  return const HomePage();
                }
                return const Scaffold(
                  body: Center(
                    child: CircularProgressIndicator(),
                  ),
                );
              });
        } else {
          return const Scaffold(
            body: Center(
              child: CircularProgressIndicator(),
            ),
          );
        }
      },
    );
  }
}

And this is the home page:

import 'package:flutter/material.dart';
import 'package:my_event_app/http/auth/user/sign_out.dart';
import 'package:my_event_app/pages/Create_Event/create_event_page.dart';
import 'package:my_event_app/pages/Onboarding/onboarding_page.dart';
import 'package:my_event_app/providers/User/user.dart';
import 'package:my_event_app/widgets/event_card_widget.dart';
import 'package:provider/provider.dart';

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Consumer<UserModel>(builder: (context, user, child) {
      return Scaffold(
        appBar: AppBar(
          leading: IconButton(
            icon: const Icon(Icons.help_outline, color: Colors.black, size: 30),
            onPressed: () {
              Navigator.push(context, MaterialPageRoute(builder: (context) {
                return const OnboardingPage();
              }));
            },
          ),
          actions: [
            IconButton(
              icon: const Icon(
                Icons.arrow_forward_ios_sharp,
                color: Colors.black,
              ),
              onPressed: () {
                signOut();
              },
            ),
          ],
          elevation: 0,
          backgroundColor: Colors.white,
        ),
        backgroundColor: Colors.white,
        body: SingleChildScrollView(
          child: Container(
            color: Colors.white,
            padding: const EdgeInsets.all(16),
            child: Column(
              children: [
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    const CircleAvatar(
                      radius: 25,
                      backgroundImage: NetworkImage(
                          "https://cdnnmundo1.img.sputniknews.com/img/07e5/09/13/1116212032_100:0:1273:1173_1920x0_80_0_0_efb734331af13dfe11ff6d43293c60e2.png"),
                    ),
                    Container(
                      height: 50,
                      width: 50,
                      decoration: BoxDecoration(
                        color: Colors.orange[400],
                        borderRadius: BorderRadius.circular(10),
                        boxShadow: [
                          BoxShadow(
                            color: Colors.black.withOpacity(0.1),
                            spreadRadius: 1,
                            blurRadius: 5,
                            offset: const Offset(0, 3),
                          ),
                        ],
                      ),
                      child: Center(
                        child: IconButton(
                          color: Colors.white,
                          onPressed: () {
                            // Navigate to add event widget
                            Navigator.push(context,
                                MaterialPageRoute(builder: (context) {
                              return const CreateEventPage();
                            }));
                          },
                          icon: const Icon(Icons.add),
                        ),
                      ),
                    ),
                  ],
                ),
                const SizedBox(height: 32),
                SizedBox(
                  width: double.infinity,
                  child: Text('Welcome, ${user.name}',
                      style: const TextStyle(
                          fontSize: 28,
                          fontWeight: FontWeight.bold,
                          fontFamily: "Roboto")),
                ),
                const SizedBox(height: 32),
                Container(
                  padding: const EdgeInsets.all(16),
                  height: 100,
                  width: double.infinity,
                  decoration: BoxDecoration(
                    color: Colors.white,
                    borderRadius: BorderRadius.circular(10),
                    boxShadow: [
                      BoxShadow(
                        color: Colors.black.withOpacity(0.1),
                        spreadRadius: 1,
                        blurRadius: 5,
                        offset: const Offset(0, 3),
                      ),
                    ],
                  ),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: [
                      Stack(alignment: Alignment.center, children: [
                        SizedBox(
                          height: 45,
                          width: 45,
                          child: CircularProgressIndicator(
                            valueColor:
                                AlwaysStoppedAnimation(Colors.orange[400]),
                            value: 14 / 20,
                            semanticsValue: "14/20",
                            color: Colors.black,
                          ),
                        ),
                        const Text("78%",
                            style: TextStyle(
                                fontSize: 14,
                                fontWeight: FontWeight.bold,
                                fontFamily: "Roboto")),
                      ]),
                      Column(
                        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: const [
                          Text("Weekly progress",
                              style: TextStyle(
                                fontSize: 14,
                                fontWeight: FontWeight.w600,
                              )),
                          Text("14/20 tasks completed"),
                        ],
                      ),
                      const Icon(Icons.bar_chart),
                    ],
                  ),
                ),
                Container(
                  margin: const EdgeInsets.symmetric(vertical: 16),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceAround,
                    children: const [
                      Text("You have 5 tasks for today",
                          style: TextStyle(
                            fontSize: 14,
                            fontWeight: FontWeight.w600,
                          )),
                      Icon(Icons.calendar_today_outlined)
                    ],
                  ),
                ),
                _renderEvents(user),
              ],
            ),
          ),
        ),
      );
    });
  }
}

Column _renderEvents(UserModel user) {
  return Column(
    children: [
      for (var event in user.events)
        EventCard(
          eventId: event,
        ),
    ],
  );
}

And here’s the provider:

import 'package:flutter/material.dart';

class UserModel extends ChangeNotifier {
  String _name = '';
  String _surnames = '';
  String _uid = '';
  String _email = '';
  List<dynamic> _events = [];

  String get name => _name;
  String get surnames => _surnames;
  String get uid => _uid;
  String get email => _email;
  List<dynamic> get events => _events;

  UserModel();

  set name(String value) {
    _name = value;
    notifyListeners();
  }

  set surnames(String value) {
    _surnames = value;
    notifyListeners();
  }

  set uid(String value) {
    _uid = value;
    notifyListeners();
  }

  set email(String value) {
    _email = value;
    notifyListeners();
  }

  set events(List<dynamic> value) {
    _events = value;
    notifyListeners();
  }

  void addEvent(String event) {
    _events.add(event);
    notifyListeners();
  }

  void removeEvent(String event) {
    _events.remove(event);
    notifyListeners();
  }

  void updateUser(String name, String uid) {
    name = name;
    uid = uid;
    notifyListeners();
  }

  void clearUser() {
    _name = '';
    _uid = '';
    notifyListeners();
  }

  Map<String, dynamic> toJson() {
    return {
      'name': _name,
      'surnames': _surnames,
      'uid': _uid,
      'email': _email,
      'events': _events
    };
  }

  fromJson(Object? json) {
    try {
      Map<dynamic, dynamic> map = json as Map<dynamic, dynamic>;
      _name = map['name'];
      _surnames = map['surnames'];
      _uid = map['uid'];
      _email = map['email'];
      _events = map['events'];
    } catch (e) {
      print(e);
    }
  }
}
```
As you can see i use Consumer in order to read data and i have a change notifier in the begginig, but it won't re render and show for example new name if i change it in fireabase.

Thank you so much!

Solution

You are using fromJson method to update values in UserModel, but it does not call notifyListeners. Add notifyListeners(); to the end of this method:

fromJson(Object? json) {
    try {
      Map<dynamic, dynamic> map = json as Map<dynamic, dynamic>;
      _name = map['name'];
      _surnames = map['surnames'];
      _uid = map['uid'];
      _email = map['email'];
      _events = map['events'];
      notifyListeners(); // add this
    } catch (e) {
      print(e);
    }
  }

Also some other things:

  • Consider declaring class UserModel with ChangeNotifier instead of class UserModel extends ChangeNotifier.
  • fromJson methods usually are acting as factory methods, meaning these return a new instance, and don’t set members in an existing instance.
  • Instead of Provider.of<UserModel>(context, listen: true).fromJson(userSnapshot.data!.data()); you can try: context.read<UserModel>().fromJson(userSnapshot.data!.data());. Here you don’t really need listening, you just want to find the provider and call fromJson. Your Consumer is the one which is listening to the changes accordingly.

Answered By – Peter Koltai

Answer Checked By – Robin (FlutterFixes Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *