The method setState() cannot be refreshed effectively in ChangedNotifier when update one of a data in List.

This issue has been tracked since 2022-11-23.

Steps to Reproduce

  1. Git repo on the code sample
  2. Click FloatingButton, and update one of gradient color.
  3. Click twice it refresh once.

Expected results:

It should be refreshed anyway.

Actual results:

I try to change the color of the Gradient dynamically, but when only one data is updated, the data is updated in even times, and the widget not refresh in odd times.

Code sample
class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    _counter++;
    /// This update is nice.
    // GradientNotifier.instance().colors = <Color>[Colors.primaries[_counter % Colors.primaries.length], Colors.red, Colors.yellow];

    /// TODO Question  Why state set incorrectly. Odd time not work.
    GradientNotifier.instance().setColor(0, Colors.primaries[_counter % Colors.primaries.length]);
    // GradientNotifier.instance().setColor(1, Colors.accents.reversed.toList()[_counter % Colors.primaries.length]);
  }

  void _setState() {
    setState(() {});
  }

  @override
  void initState() {
    super.initState();
    GradientNotifier.instance().addListener(_setState);
  }

  @override
  void dispose() {
    GradientNotifier.instance().removeListener(_setState);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: DecoratedBox(
        decoration: BoxDecoration(
            gradient: LinearGradient(
          colors: GradientNotifier.instance().colors,
        )),
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              const Text(
                'You have pushed the button this many times:',
              ),
              Text('${GradientNotifier.instance().colors[0].toString()}, ${GradientNotifier.instance().colors[1].toString()}'),
              Text(
                '$_counter',
                style: Theme.of(context).textTheme.headline4,
              ),
            ],
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

class GradientNotifier with ChangeNotifier {
  GradientNotifier._();

  static final GradientNotifier _instance = GradientNotifier._();

  factory GradientNotifier.instance() => _instance;

  /// Gradient Colors
  List<Color>? _colors;

  List<Color> get colors => _colors ?? <Color>[Colors.blue, Colors.white];

  set colors(List<Color> colors) {
    _colors = colors;
    notifyListeners();
  }

  setColor(int index, Color? color) {
    if (color == null) return;
    colors[index] = color;
    print("after $colors");
    List<Color> temp = colors.toList(growable: true);
    _colors = temp;
    notifyListeners();
  }
}
Logs
exaby73 wrote this answer on 2022-11-24

Hello @davidzou. This is working as expected. Basically, Flutter is trying to optimize builds. The List you are providing has the same memory address so Flutter thinks the value has not changed and therefore doesn't update the UI. You can fix it by wrapping it in `List.from':

decoration: BoxDecoration(  
  gradient: LinearGradient(
    colors: List.from(GradientNotifier.instance().colors),
  ),
),

Since this is working as intended, I am closing this issue. Thank you

davidzou wrote this answer on 2022-11-25

Thanks @exaby73

More Details About Repo
Owner Name flutter
Repo Name flutter
Full Name flutter/flutter
Language Dart
Created Date 2015-03-06
Updated Date 2022-12-07
Star Count 147031
Watcher Count 3560
Fork Count 23915
Issue Count 11300

YOU MAY BE INTERESTED

Issue Title Created Date Updated Date