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 getref.watch
there - I have tried
StateNotifier
instead ofNotifier
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.