I'm using Bloc/Cubit (flutter_bloc) & Auto_Router packages
I have the counter on the /counter route and FloatingActionButtons on a /user_profile route to increment the counter.
How can I increment the counter from a different page/route? I should be able to switch back and forth between pages and increment/decrement from either page but I'm getting the following error message:
Exception has occurred. ProviderNotFoundException (Error: Could not find the correct Provider above this UserProfilePage Widget
This happens because you used a BuildContext
that does not include the provider
of your choice. There are a few common scenarios:
You added a new provider in your
main.dart
and performed a hot-reload. To fix, perform a hot-restart.The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then other routes will not be able to access that provider.
You used a
BuildContext
that is an ancestor of the provider you are trying to read.Make sure that UserProfilePage is under your MultiProvider/Provider. This usually happens when you are creating a provider and trying to read it immediately.
For example, instead of:
Widget build(BuildContext context) { return Provider<Example>( create: (_) => Example(), // Will throw a ProviderNotFoundError, because `context` is associated // to the widget that is the parent of `Provider<Example>` child: Text(context.watch<Example>()), ), }
consider using
builder
like so:Widget build(BuildContext context) { return Provider<Example>( create: (_) => Example(), // we use `builder` to obtain a new `BuildContext` that has access to the provider builder: (context) { // No longer throws return Text(context.watch<Example>()), } ), }
If none of these solutions work, consider asking for help on StackOverflow: https://stackoverflow.com/questions/tagged/flutter )
Here is my code:
home_page.dart
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return AutoTabsScaffold(
appBarBuilder: (_, tabsRouter) => AppBar(
backgroundColor: Colors.indigo,
title: const Text('FlutterBottomNav'),
centerTitle: true,
leading: const AutoBackButton(),
),
backgroundColor: Colors.teal,
routes: const [
CounterRouter(),
PostsRouter(),
UsersRouter(),
SettingsRouter(),
],
bottomNavigationBuilder: (_, tabsRouter) {
return SalomonBottomBar(
margin: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 40,
),
currentIndex: tabsRouter.activeIndex,
onTap: tabsRouter.setActiveIndex,
items: [
SalomonBottomBarItem(
selectedColor: Colors.amberAccent,
icon: const Icon(Icons.plus_one, size: 30),
title: const Text('Counter'),
),
SalomonBottomBarItem(
selectedColor: Colors.amberAccent,
icon: const Icon(Icons.post_add, size: 30),
title: const Text('Posts'),
),
SalomonBottomBarItem(
selectedColor: Colors.blue[200],
icon: const Icon(
Icons.person,
size: 30,
),
title: const Text('Users'),
),
SalomonBottomBarItem(
selectedColor: Colors.pinkAccent[100],
icon: const Icon(
Icons.settings,
size: 30,
),
title: const Text('Settings'),
),
],
);
},
);
}
}
counter_cubit.dart
class CounterCubit extends Cubit<int> {
CounterCubit() : super(0);
void increment() => emit(state 1);
void decrement() => emit(state - 1);
}
counter_page.dart
class CounterPage extends StatelessWidget {
const CounterPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => CounterCubit(),
child: const CounterView(),
);
}
}
class CounterView extends StatelessWidget {
const CounterView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
return Scaffold(
appBar: AppBar(title: Text(l10n.counterAppBarTitle)),
body: const Center(child: CounterText()),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
FloatingActionButton(
key: const Key('counterView_star_floatingActionButton'),
onPressed: () {
print('star');
},
child: const Icon(Icons.star),
),
const SizedBox(height: 8),
FloatingActionButton(
key: const Key('counterView_boat_floatingActionButton'),
onPressed: () {
print('boat');
},
child: const Icon(Icons.sailing),
),
FloatingActionButton(
key: const Key('counterView_increment_floatingActionButton'),
onPressed: () => context.read<CounterCubit>().increment(),
child: const Icon(Icons.add),
),
const SizedBox(height: 8),
FloatingActionButton(
key: const Key('counterView_decrement_floatingActionButton'),
onPressed: () => context.read<CounterCubit>().decrement(),
child: const Icon(Icons.remove),
),
],
),
);
}
}
class CounterText extends StatelessWidget {
const CounterText({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final count = context.select((CounterCubit cubit) => cubit.state);
return Text('$count', style: theme.textTheme.headline1);
}
}
user_profile_page.dart
class UserProfilePage extends StatelessWidget {
final int userId;
const UserProfilePage({
Key? key,
@PathParam() required this.userId,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final user = User.users[userId - 1];
return Scaffold(
backgroundColor: user.color,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
UserAvatar(
avatarColor: Colors.white,
username: 'user${user.id}',
)
],
),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
FloatingActionButton(
key: const Key('counterView_star_floatingActionButton'),
onPressed: () {
print('star');
},
child: const Icon(Icons.star),
),
const SizedBox(height: 8),
FloatingActionButton(
key: const Key('counterView_boat_floatingActionButton'),
onPressed: () {
print('boat');
},
child: const Icon(Icons.sailing),
),
FloatingActionButton(
key: const Key('counterView_increment_floatingActionButton'),
onPressed: () => context.read<CounterCubit>().increment(),
child: const Icon(Icons.add),
),
const SizedBox(height: 8),
FloatingActionButton(
key: const Key('counterView_decrement_floatingActionButton'),
onPressed: () => context.read<CounterCubit>().decrement(),
child: const Icon(Icons.remove),
),
],
),
);
}
}
any help greatly appreciated. thanks!
CodePudding user response:
Please refer to below code
ValueNotifer & ValueListenableBuilder can be used to hold value and update widget by notifying its listeners and reducing number of times widget tree getting rebuilt.
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
final ValueNotifier<int> counter = ValueNotifier(0);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: FloatingActionButtonClass(),
);
}
}
class FloatingActionButtonClass extends StatelessWidget {
void _incrementCounter() {
counter.value ;
}
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
body: Center(
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MyHomePage()),
);
},
child: Text("Floating Action Button"),
),
),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({
Key? key,
}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Example"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
ValueListenableBuilder(
valueListenable: counter,
builder: (context, value, child) {
return Text(
counter.value.toString(),
style: Theme.of(context).textTheme.headline4,
);
},
),
],
),
),
);
}
}
CodePudding user response:
I needed to create a BlocProvider and move the view to a child.
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => CounterCubit(),
child: const HomePageView(),
);
}
}
class HomePageView extends StatelessWidget {
const HomePageView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return AutoTabsScaffold(
appBarBuilder: (_, tabsRouter) => AppBar(
backgroundColor: Colors.indigo,
title: const Text('FlutterBottomNav'),
centerTitle: true,
leading: const AutoBackButton(),
),
backgroundColor: Colors.teal,
routes: const [
CounterRouterB(),
CounterRouter(),
PostsRouter(),
UsersRouter(),
SettingsRouter(),
],
bottomNavigationBuilder: (_, tabsRouter) {
return SalomonBottomBar(
margin: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 40,
),
currentIndex: tabsRouter.activeIndex,
onTap: tabsRouter.setActiveIndex,
items: [
SalomonBottomBarItem(
selectedColor: Colors.redAccent,
icon: const Icon(Icons.plus_one, size: 30),
title: const Text('Counter2'),
),
SalomonBottomBarItem(
selectedColor: Colors.amberAccent,
icon: const Icon(Icons.plus_one, size: 30),
title: const Text('Counter'),
),
SalomonBottomBarItem(
selectedColor: Colors.amberAccent,
icon: const Icon(Icons.post_add, size: 30),
title: const Text('Posts'),
),
SalomonBottomBarItem(
selectedColor: Colors.blue[200],
icon: const Icon(
Icons.person,
size: 30,
),
title: const Text('Users'),
),
SalomonBottomBarItem(
selectedColor: Colors.pinkAccent[100],
icon: const Icon(
Icons.settings,
size: 30,
),
title: const Text('Settings'),
),
],
);
},
);
}
}