Search code examples
flutterriverpod

Flutter riverpod - why I must use hot reload to get UI updated?


I use flutter riverpod for managing state. Function lightUpPlayTitle() is executed on tap with ref.read(titleBlurProvider.notifier).lightUpPlayTitle(), I can see in console it was from prints, but UI is updated only after hot reload. Why I must use hot reload to get UI updated? What mistake did I do?

full code

class StartPage extends StatelessWidget {
  const StartPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          Image.asset('assets/matches_background.jpeg'),
          const TitlesWidget()
        ],
      ),
    );
  }
}

class TitlesWidget extends StatelessWidget {
  const TitlesWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          TitleWidget(
            text: 'Play',
            index: 0,
            callOnTap: () {
              Navigator.pushReplacement(
                  context,
                  MaterialPageRoute(
                      builder: ((context) => const PositionedPage())));
            },
          ),
          TitleWidget(
            text: 'Leads',
            index: 1,
            callOnTap: () {},
          )
        ],
      ),
    );
  }
}

class TitleWidget extends ConsumerWidget {
  TitleWidget(
      {super.key,
      required this.text,
      required this.callOnTap,
      required this.index});
  String text;
  Function callOnTap;

  int index = 0;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final double opacity = ref.watch(titleBlurProvider)[index];
    return Consumer(
      builder: (context, ref, child) => Stack(
        children: [
          Opacity(
            opacity: opacity,
            child: ImageFiltered(
              imageFilter: ImageFilter.blur(sigmaX: 3, sigmaY: 3),
              child: Text(
                text,
                style: const TextStyle(
                    fontSize: 80,
                    fontWeight: FontWeight.bold,
                    color: Colors.white),
                textAlign: TextAlign.center,
              ),
            ),
          ),
          GestureDetector(
            onLongPress: () => debugPrint(opacity.toString()),
            onTap: () =>
                ref.read(titleBlurProvider.notifier).lightUpPlayTitle(),
            child: Opacity(
              opacity: 0.8,
              child: Text(
                text,
                style: const TextStyle(
                    fontSize: 80,
                    fontWeight: FontWeight.bold,
                    color: Colors.white),
                textAlign: TextAlign.center,
              ),
            ),
          ),
        ],
      ),
    );
  }
}

final titleBlurProvider = StateNotifierProvider<TitleBlur, List<double>>(
  (ref) => TitleBlur(),
);

class TitleBlur extends StateNotifier<List<double>> {
  TitleBlur() : super([0, 0]);

  startRegularBlur() {
    Timer.periodic(const Duration(), (timer) {});
  }

  lightUpPlayTitle() {
    debugPrint('light up');

    while (state[0] < 1) {
      Future.delayed(const Duration(milliseconds: 500));
      state[0] = state[0] + 0.1;
      if (state[0] > 1) state[0] = 1;
      debugPrint(state[0].toString());
    }
  }
}


I tried moving Consumer to the top of widget tree but it didn't help me

Solution

  • You are mutating the list by changing the element in this line:

    state[0] = state[0] + 0.1;
    

    The state of a StateNotifier should be immutable, so you have to clone the list and add the modification. You can do it like this:

    state = [state[0] + 0.1, ...state.sublist(1)]
    

    See about immutability of the state of StateNotifier and how to modify the state here: https://riverpod.dev/docs/providers/state_notifier_provider


    But please note that StateNotifier is an old concept and is no longer recommended. The docs I linked above is in the "old" providers section. Please refer to the rest of Riverpod documentation for guides on the latest way to manage states using Riverpod.