Home > Back-end >  UI does not get updated when using `Notifier`
UI does not get updated when using `Notifier`

Time:11-09

I basically have a four-page sample app that I'm just getting started with, and I developed a custom bottom navigation bar. However, when I switch pages (using PageView and Riverpod) or tap on the BlurBottomNav buttons, it changes pages but the BlurBottomNav UI doesn't get updated. I am using flutter_riverpod: ^2.1.1

What I have tried-

  • I have tried to convert NavButton from stateless to consumer and get ref.watch there
  • I have tried StateNotifier instead of Notifier

Update: Same Code works when using ChangeNotifier with ChangeNotifierProvider but it doesn't with Notifier

Here are snippets

pageview_widget.dart

import 'package:demo/src/constants/nav_items.dart';
import 'package:demo/src/features/categories/presentation/categories_page.dart';
import 'package:demo/src/features/favourites/presentation/fav_page.dart';
import 'package:demo/src/features/pageview/data/pageview_service.dart';
import 'package:demo/src/features/pageview/presentation/bottom_nav/bottom_nav.dart';
import 'package:demo/src/features/settings/presentation/settings_page.dart';
import 'package:demo/src/features/wallpaper/presentation/wall_list.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class PageViewWidget extends StatefulWidget {
  const PageViewWidget({super.key});

  @override
  State<PageViewWidget> createState() => _PageViewWidgetState();
}

class _PageViewWidgetState extends State<PageViewWidget> {
  final pageController = PageController(initialPage: 0);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('Breezy'),
        ),
        body: Consumer(
          builder: (context, ref, child) {
            final pageviewService = ref.watch(pageviewServiceProvider.notifier);
            final pageviewServiceRead = ref.watch(pageviewServiceProvider);
            return Stack(
              children: [
                PageView.builder(
                    itemCount: 4,
                    controller: pageController,
                    onPageChanged: (int index) {
                      pageviewService.onPageChanged(index);
                      // pageviewService.changeShowBNB(true);
                    },
                    itemBuilder: (context, index) {
                      switch (index) {
                        case 0:
                          return const WallpaperList();
                        case 1:
                          return const CategoriesPage();
                        case 2:
                          return const FavouritesPage();
                        case 3:
                          return const SettingsPage();
                        default:
                          // Should never get hit.
                          return CircularProgressIndicator(
                            color: Theme.of(context).primaryColor,
                          );
                      }
                    }),
                Positioned.fill(
                  bottom: 20,
                  child: SafeArea(
                    child: BlurBottomNav(
                      onItemSelected: (int value) {
                        pageviewService.onTapByBnb(value, pageController);
                      },
                      selectedIndex: pageviewServiceRead.currentindex,
                      items: navItems,
                    ),
                  ),
                ),
              ],
            );
          },
        ));
  }
}

here is pageview_service.dart

import 'package:demo/src/features/pageview/domain/pageview_navigation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class PageViewService extends Notifier<PageViewNavigation> {
  @override
  PageViewNavigation build() {
    return PageViewNavigation(
        currentindex: 0, changedByBNB: false, showBNB: true);
  }

  void changeShowBNB(bool value) {
    state.showBNB = value;
  }

  void changeIndex(int index) {
    state.currentindex = index;
  }

  void _changeBNBBool(bool value) {
    state.changedByBNB = value;
  }

  void onPageChanged(int index) {
    if ((index != state.currentindex) && (!state.changedByBNB)) {
      changeIndex(index);
    } else {
      _changeBNBBool(false);
    }
  }

  void onTapByBnb(int index, PageController pageController) {
    if (index != state.currentindex) {
      if (pageController.hasClients) {
        _changeBNBBool(true);
        changeIndex(index);
        pageController.animateToPage(
          index,
          curve: Curves.fastOutSlowIn,
          duration: const Duration(milliseconds: 350),
        );
      }
    }
  }
}

final pageviewServiceProvider =
    NotifierProvider<PageViewService, PageViewNavigation>(PageViewService.new);

here is bottom_nav.dart

import 'dart:ui';

import 'package:demo/src/features/pageview/domain/nav_button.dart';
import 'package:demo/src/features/pageview/presentation/bottom_nav/nav_buttons.dart';
import 'package:flutter/material.dart';

class BlurBottomNav extends StatelessWidget {
  final List<BottomNavBarItem> items;
  final ValueChanged<int> onItemSelected;
  final int selectedIndex;
  const BlurBottomNav({
    super.key,
    required this.items,
    required this.onItemSelected,
    required this.selectedIndex,
  });

  @override
  Widget build(BuildContext context) {
    return Align(
      alignment: Alignment.bottomCenter,
      child: SizedBox(
        width: MediaQuery.of(context).size.width * 0.65,
        height: 57,
        child: ClipRRect(
          borderRadius: BorderRadius.circular(15),
          child: BackdropFilter(
            filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
            child: Container(
              height: 100,
              color: Colors.red,
              child: Padding(
                padding: const EdgeInsets.all(8.0),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  children: items.map((item) {
                    var index = items.indexOf(item);
                    return GestureDetector(
                      onTap: () => onItemSelected(index),
                      child: NavButton(
                        item: item,
                        isSelected: index == selectedIndex,
                      ),
                    );
                  }).toList(),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

here is bottom_nav_button.dart

import 'dart:ui';

import 'package:demo/src/features/pageview/domain/nav_button.dart';
import 'package:flutter/material.dart';

class NavButton extends StatelessWidget {
  final BottomNavBarItem item;
  final bool isSelected;
  const NavButton({
    super.key,
    required this.item,
    required this.isSelected,
  });

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        ClipRRect(
          borderRadius: BorderRadius.circular(15),
          child: BackdropFilter(
            filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
            child: AnimatedContainer(
              duration: const Duration(milliseconds: 200),
              height: 100,
              width: 43,
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(15),
                border: Border.all(
                  color: Colors.purple,
                  width: 2,
                ),
                color: isSelected ? item.activeIconColor : Colors.white,
              ),
              child: item.icon,
            ),
          ),
        ),
      ],
    );
  }
}

and lastly here is BottomNavBarItem

import 'package:flutter/material.dart';

class BottomNavBarItem {
  BottomNavBarItem({
    required this.icon,
    required this.activeIconColor,
  });

  final Widget icon;
  final Color activeIconColor;
}

I haven't used flutter in a while, so please be gentle with me

CodePudding user response:

Keep in mind that you can't use mutable state like this in your Notifier subclasses:

state.showBNB = value;

Instead it should be:

state = /* new state */

If you have a custom state class, make sure all properties are final and add a copyWith method.

  • Related