如何在 Flutter 中增加另一个 route/page 的计数器
How to increment a counter from another route/page in Flutter
我正在使用 Bloc/Cubit (flutter_bloc) & Auto_Router 软件包
我在 /counter 路由上有计数器,在 /user_profile 路由上有 FloatingActionButtons 来增加计数器。
如何从另一个 page/route 递增计数器?我应该能够在页面之间来回切换并从任一页面 increment/decrement 但我收到以下错误消息:
Exception has occurred.
ProviderNotFoundException (Error: Could not find the correct Provider above this UserProfilePage Widget
发生这种情况是因为您使用的 BuildContext
不包括提供商
你的选择。有几个常见的场景:
您在 main.dart
中添加了一个新提供程序并执行了热重载。
要修复,请执行热重启。
您尝试读取的提供商在不同的路径上。
提供商是“范围内的”。因此,如果您在路由中插入提供者,则
其他路由将无法访问该提供商。
您使用的 BuildContext
是您尝试读取的提供商的祖先。
确保 UserProfilePage 在您的 MultiProvider/Provider 下。
这通常发生在您创建提供程序并尝试立即读取它时。
例如,而不是:
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>()),
),
}
考虑像这样使用 builder
:
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>()),
}
),
}
如果 none 这些解决方案有效,请考虑在 Whosebug 上寻求帮助:
https://whosebug.com/questions/tagged/flutter
)
这是我的代码:
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),
),
],
),
);
}
}
非常感谢任何帮助。谢谢!
请参考以下代码
ValueNotifer 和 ValueListenableBuilder 可用于保存值并通过通知其侦听器并减少重建小部件树的次数来更新小部件。
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,
);
},
),
],
),
),
);
}
}
我需要创建一个 BlocProvider 并将视图移至子视图。
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'),
),
],
);
},
);
}
}
我正在使用 Bloc/Cubit (flutter_bloc) & Auto_Router 软件包
我在 /counter 路由上有计数器,在 /user_profile 路由上有 FloatingActionButtons 来增加计数器。
如何从另一个 page/route 递增计数器?我应该能够在页面之间来回切换并从任一页面 increment/decrement 但我收到以下错误消息:
Exception has occurred. ProviderNotFoundException (Error: Could not find the correct Provider above this UserProfilePage Widget
发生这种情况是因为您使用的 BuildContext
不包括提供商
你的选择。有几个常见的场景:
您在
main.dart
中添加了一个新提供程序并执行了热重载。 要修复,请执行热重启。您尝试读取的提供商在不同的路径上。
提供商是“范围内的”。因此,如果您在路由中插入提供者,则 其他路由将无法访问该提供商。
您使用的
BuildContext
是您尝试读取的提供商的祖先。确保 UserProfilePage 在您的 MultiProvider/Provider 下。 这通常发生在您创建提供程序并尝试立即读取它时。
例如,而不是:
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>()), ), }
考虑像这样使用
builder
: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>()), } ), }
如果 none 这些解决方案有效,请考虑在 Whosebug 上寻求帮助: https://whosebug.com/questions/tagged/flutter )
这是我的代码:
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),
),
],
),
);
}
}
非常感谢任何帮助。谢谢!
请参考以下代码
ValueNotifer 和 ValueListenableBuilder 可用于保存值并通过通知其侦听器并减少重建小部件树的次数来更新小部件。
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,
);
},
),
],
),
),
);
}
}
我需要创建一个 BlocProvider 并将视图移至子视图。
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'),
),
],
);
},
);
}
}