Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to listen for change within a list using flutter provider?

I have a provider that is listening for changes to a playerList class:

import 'package:flutter/material.dart';
import 'package:scam_artist/constants/PlayerColors.dart';
import 'package:scam_artist/models/Player.dart';

class PlayerList with ChangeNotifier {
  List<Player> _players = [];

  List<Player> get players {
    return [..._players];
  }

  void addPlayer(Key key, String name, int index) {
    _players.add(
        new Player(key: key, color: PlayerColors.colors[index], name: name));
    notifyListeners();
  }

  void editPlayerName(String newName, int index) {
    _players[index].name = newName;
    notifyListeners();
  }

  void editPlayerColor(Color newColor, int index) {
    _players[index].color = newColor;
    notifyListeners();
  }
}

However, when I call a function to change a value to one of the Player objects (change name for example), the list doesn't update the object with new data.

Do I need another provider for the Player class? If so, how do I make the PlayerList provider listen for changes in the Player provider?

Doing a little research, I'm thinking ProxyProvider might be what I'm looking for, but I'm not sure how to implement it.

Here's my Player class if that is helpful:

import 'package:flutter/material.dart';

class Player {
  // id will be for database if implemented
  String uid;
  Key key;
  String name;

  //position is the order where the player plays
  int position;
  Color color;
  bool isTurn;
  bool isFakeArtist;
  bool isWinner;

  Player({this.key, this.name, this.color});
}

And this is where I create the ChangeNotifierProvider:

import 'package:flutter/material.dart';
import 'package:scam_artist/UserListener.dart';
import 'package:scam_artist/models/user.dart';
import 'package:scam_artist/providers/PlayerList.dart';
import 'package:scam_artist/services/AuthService.dart';
import 'package:provider/provider.dart';
import 'package:scam_artist/views/Lobby.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        StreamProvider<User>.value(value: AuthService().user),
        ChangeNotifierProvider(create: (context) => PlayerList())
      ],
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: UserListener(),
        routes: {Lobby.routeName: (ctx) => Lobby()},
      ),
    );
  }
}
like image 356
quazi_moto Avatar asked May 31 '20 20:05

quazi_moto


2 Answers

There are multiple things wrong here.

1. Fix your PlayerList declaration line.

//CORRECT
class PlayerList extends ChangeNotifier {...}
//WRONG
class PlayerList with ChangeNotifier {...}

4. Fix your players getter line.

Use this:

List<Player> get players => _players;

3. You need more Conceptual Knowledge on Provider.


Basically, Provider makes your PlayerList accessible from anywhere within the Widget Tree below where you have provided it. For example, you are providing from the top of your MaterialApp. So you can access it in your HomePage or Lobby.

To access your PlayerList, you have to use a Consumer widget or Selector widget, but for your case, Consumer is enough. Selector is for advanced usage.

Here is the code for reading live values from your PlayerList.

class Lobby extends StatelessWidget {

  @override
  Widget build(BuildContext context) => Scaffold(
    body: _body(),
  );

  _body() => Container(
    child: Consumer<PlayerList>(
      builder: (BuildContext context, PlayerList bloc) => 
          ListView.builder(
            itemCount: bloc.players.length,
            itemBuilder: 
               (BuildContext context, int index) => Text(bloc.players[index].name),
          ),
    ),

  );

}
like image 83
hysabone.com Avatar answered Nov 07 '22 04:11

hysabone.com


I am having exactly the same problem.

One way I solved it (not the prettiest though) is to have a method in the ChangeNotifier to return a specific object and then watch for changes, e.g.

// in ChangeNotifier

Player getPlayer(int index) => _players[index];

and then in the widget have

context.watch<PlayerList>().getPlayer(index).updateName("newName")
like image 20
alexlipa Avatar answered Nov 07 '22 03:11

alexlipa