listener of ChangeNotifierProvider not being updated

Issue

I’ve tried multiple implementations of ChangeNotifierProvider (final xyz=provider.of<DigitalHeroes>(context), Consumer<DigitalHeroes>..), and I’m unable to recieve the updated list in my listener/consumer. It always returns an empty list.

I/flutter ( 4410): []

In my splash screen I’m calling DigitalHeroes().getHeroes() from providers/digital_heroes.dart. It calls the function and prints the length of the new list of heroes from the cms which is 1, so I know the _heroes list is being updated inside the provider class.

Below is the existing implementation.

models/digital_hero.dart

class DigitalHero {
  String name;
  String title;
  String title_extended;
  String phone_number;
  String email_address;
  double location_lat;
  double location_long;
  String image;
  String bio;
  Map<String, dynamic> related_projects;
  DigitalHero({
    this.name,
    this.title,
    this.title_extended,
    this.phone_number,
    this.email_address,
    this.location_lat,
    this.location_long,
    this.image,
    this.bio,
    this.related_projects,
  });

  static DigitalHero fromJson(Map<String, dynamic> json) {
    return DigitalHero(
      name: json['name'] as String,
      title: json['title'] as String,
      title_extended: json['title_extended'] as String,
      phone_number: json['phone_number'] as String,
      email_address: json['email_address'] as String,
      location_lat: json['location_lat'] as double,
      location_long: json['location_long'] as double,
      image: json['image'] as String,
      bio: json['bio'] as String,
      related_projects: json['related_projects'] as Map<String, dynamic>,
    );
  }

  Map<String, dynamic> toJson() => {
        'name': name,
        'title': title,
        'title_extended': title_extended,
        'phone_number': phone_number,
        'email_address': email_address,
        'location_lat': location_lat,
        'location_long': location_long,
        'image': image,
        'bio': bio,
        'related_projects': related_projects,
      };
}

providers/digital_heroes.dart:

class DigitalHeroes with ChangeNotifier {
  var singleton = Singleton();
List<DigitalHero> _heroes = [];

  List<DigitalHero> get heroes {
    return [..._heroes];
  }

  void getHeroes() async {
    await singleton.init();
    try {
      var result = await singleton.contentChef
          .getPreviewChannel(
            apiKey: 'XYZ',
            publishingChannel: 'XYZ',
            status: PublishingStatus.stage,
          )
          .searchContents<DigitalHero>(
              filters: SearchContentsFilters(
                skip: 0,
                take: 10,
                contentDefinition: ['digital-hero'],
              ),
              fromJson: DigitalHero.fromJson);

      var json = jsonDecode(jsonEncode(result));
      List<DigitalHero> heroes = [];
      for (var item in json['items']) {
        heroes.add(DigitalHero.fromJson(item['payload']));
      }

      _heroes = heroes;
      print(_heroes.length); // prints 1, which is the correct length of the list of heroes from cms
      notifyListeners();
    } catch (e) {
      print(e);
    } finally {
      //notifyListeners(); // also tried notifying here in case it was an async issue, list still empty
    }
  }

// void getHeroes() { //also tried this, list still empty
  //   _heroes = [
  //     DigitalHero(
  //         bio: "hi",
  //         name: "hi",
  //         email_address: "hi",
  //         image: "null",
  //         location_lat: 5)
  //   ];
  //   notifyListeners();
  // }
}

screens/digital_heroes/digital_heros.dart

class DigitalHeroesScreen extends StatefulWidget {
  static const routeName = 'digital_heroes';

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

class _DigitalHeroesScreenState extends State<DigitalHeroesScreen> {
  var loading = false;
  // var singleton = Singleton();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.white,
        leading: IconButton(
          icon: Icon(
            Icons.search,
            color: Colors.black,
          ),
          onPressed: () {
            showSearch(
              context: context,
              delegate: CustomSearchDelegate(),
            );
          },
        ),
        title: Text(
          'Digital Heroes',
          style: TextStyle(color: Colors.black),
        ),
        actions: [
          IconButton(
            icon: Icon(
              Icons.person,
              color: Colors.black,
            ),
            // color: Colors,
            onPressed: () {
              showModalBottomSheet(
                  shape: RoundedRectangleBorder(
                    borderRadius:
                        BorderRadius.vertical(top: Radius.circular(12.0)),
                  ),
                  context: context,
                  builder: (context) {
                    return Container(
                      child: buildModalBottomProfile(),
                    );
                  });
            },
          )
        ],
      ),
      body: HeroList(),
      // ListView.builder(
      //   itemBuilder: (context, index) =>
      //       DigitalHeroItem(digitalHeroes.heroes[index]),
      //   itemCount: digitalHeroes.heroes.length,
      // );
    );
  }
}

class HeroList extends StatelessWidget {
  const HeroList({
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final heroesData = Provider.of<DigitalHeroes>(context);
    final heroes = heroesData.heroes; //calls get method in heroes provider
    return InkWell(
      child: Text("click to print provided list of heroes"),
      onTap: () => {print(heroes)}, //prints empty list '[]' 
    );
  }
}

main.dart

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    return ChangeNotifierProvider<DigitalHeroes>( //tried with multiprovider, no change
      create: (ctx) => DigitalHeroes(),
      child: MaterialApp(
        title: 'xyz',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        debugShowCheckedModeBanner: false,
        home: MyHomePage(title: 'Home'),
        routes: {
          Splash.routeName: (ctx) => Splash(),
          Onboarding.routeName: (ctx) => Onboarding(),
          Login.routeName: (ctx) => Login(),
          BottomTabsScreen.routeName: (ctx) => BottomTabsScreen(),
          Home.routeName: (ctx) => Home(),
          DigitalHeroesScreen.routeName: (ctx) => DigitalHeroesScreen(),
          
        },
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    Singleton.instance.screenSize = MediaQuery.of(context).size;
    return Container(
      child: Padding(
        padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
        child: Splash(),
      ),
    );
  }
}

inside screens/splash.dart

void initData() async {
    setState(() {
      _loading = true;
    });
    // ignore: await_only_futures
    await DigitalHeroes().getHeroes();

    setState(() {
      _loading = false;
    });
    if (!_loading && !_animating) {
      Navigator.of(context).pushReplacementNamed(Onboarding.routeName);
    }
  }

Solution

I was able to figure it out finally. I was creating a new intance of DigitalHeroes with

await DigitalHeroes().getHeroes();

instead of using the inherited instance like

await Provider.of<DigitalHeroes>(context, listen: false).getHeroes();

or calling the async code in the constuctor of the provider like

class Heroes extends ChangeNotifier {
  List _heroes;

  void getData() async {
    // Loading Assets
   _heroes=heroes;
    notifyListeners();
  }

  Heroes() {
    getData();
  }
}

Answered By – jacob blankenship

Answer Checked By – David Goodson (FlutterFixes Volunteer)

Leave a Reply

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