flutter - Flutter 通过提供者访问推送页面上的通知程序
问题描述
应用说明
我的应用程序包含 3 个可在我的 BottomNavigationBar 中访问的主页。当我在菜单之间导航时,我希望我的页面在每次调用时都被初始化。
在带有我的表单的Menu1中,我可以提交我的表单。这将打开一个带有 pushNamed 的页面,我们将其称为SubMenu1。此页面显示基于表单数据计算的结果。在这个页面上,我可以返回 pop 并且什么都不做,或者我有一个按钮,它必须更新我表单的某些值。
问题
如何从SubMenu1更新Menu1表单的值?实际上,当您推送一个页面时,它会在MaterialApp级别结束,因此无法从Menu1访问我的通知程序。解决方案是然后通过MaterialApp上面的 Notifier 。但是突然之间,我的表单不再在每次调用Menu1时都被初始化,而在应用程序启动时只有一次......
代码
在提供的代码中,我将通知放在MaterialApp上方。所以我可以访问我在SubMenu1中的 Notifier并且我可以修改Menu1中字段的值,但是当我从导航栏更改页面并返回值仍然存在时,页面还没有初始化。
应用屏幕:
class AppScreen extends StatelessWidget {
const AppScreen({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<LicenseNotifier>(
create: (_) => LicenseNotifier(),
child: ChangeNotifierProvider<BottomNavigationBarNotifier>( // Here my navigation
create: (BuildContext context) => BottomNavigationBarNotifier(),
child: ChangeNotifierProvider<Menu1Notifier>( // Here my form Notifier for Menu 1
create: (BuildContext context) => Menu1Notifier(),
child: MaterialApp(
title: AppConfig.APPLICATION_NAME,
debugShowCheckedModeBanner: false,
theme: AppTheme().data,
initialRoute: AppRoutes.HOME,
onGenerateRoute: RoutesClass.generate,
),
)
)
);
}
}
我的 BottomNavigationBarNotifier :
class BottomNavigationBarNotifier with ChangeNotifier {
int currentIndex = AppConfig.NAVIGATION_DEFAULT_INDEX;
BottomNavigationBarNotifier();
Future<void> navigationScreenIndex({int index}) async{
currentIndex = index;
notifyListeners();
}
Widget loadScreenWithNavigation()
{
switch (currentIndex)
{
case 0:
return Menu1Screen(title: 'Menu 1');
break;
case 1:
return Menu2Screen(title: 'Menu 2');
break;
case 2:
return Menu3Screen(title: 'Menu 3');
break;
default:
return Menu1Screen(title: 'Menu 1');
break;
}
}
}
我的Menu1Screen通知程序:
class Menu1Notifier extends FormNotifier {
TextEditingController controllerTest;
Menu1Notifier (){
_initialise();
}
Future _initialise() async{
controllerTest = TextEditingController();
notifyListeners();
}
}
解决方案
我不确定我是否从上面的代码片段中完全理解你在做什么,但我想我理解这个要求:
- Menu1 有状态
- SubMenu1 应该能够读取和更新 Menu1 状态
- Menu1 调用 SubMenu1 作为“子”屏幕,没有其他路径到 SubMenu1
处理这种简单状态的最简单方法是让 Menu1 在显示时将其状态传递给 SubMenu1(作为构造函数参数),并让 SubMenu1 直接更新它。我在下面显示了示例代码。当然,这是两个屏幕之间的紧密耦合,但它似乎是一个紧密耦合的要求。
Provider 包可用于将状态与 UI 分开。您可以使用这种方法。您说“但是突然之间,我的表单不再在每次调用 Menu1 时都被初始化,而在应用程序启动时只有一次”。Menu1 可以在调用时调用一个方法来初始化 Provider 吗?我为 Provider 方法添加了示例代码。一些简短的说明:
- 我使用 MultiProvider,即使我只有一个 ChangeNotifierProvider,这将使您的代码更易于阅读
- ChangeNotifier FormData 保存表单状态
- 父表单初始化表单状态
- 两种形式都调用 FormData 方法来设置和获取状态
长示例代码直接状态如下:
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: FormTest(),
),
),
);
}
}
class FormTest extends StatefulWidget {
@override
_FormTestState createState() => _FormTestState();
}
class _FormTestState extends State<FormTest> {
int _selectedIndex = 0;
final List<Widget> _options = [
FormWidget(),
PlaceholderWidget(Colors.deepOrange),
PlaceholderWidget(Colors.green)
];
void _onItemTap(int index) {
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Example'),
backgroundColor: Colors.teal),
body: Center(
child: _options.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
backgroundColor: Colors.teal),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
backgroundColor: Colors.cyan),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'Settings',
backgroundColor: Colors.lightBlue,
),
],
type: BottomNavigationBarType.shifting,
currentIndex: _selectedIndex,
selectedItemColor: Colors.white,
unselectedItemColor: Colors.grey,
iconSize: 40,
onTap: _onItemTap,
elevation: 5),
);
}
}
class PlaceholderWidget extends StatelessWidget {
final Color color;
PlaceholderWidget(this.color);
@override
Widget build(BuildContext context) {
return Container(
color: color,
);
}
}
class FormWidget extends StatefulWidget {
@override
FormWidgetState createState() {
return FormWidgetState();
}
}
class FormWidgetState extends State<FormWidget> {
final _formKey = GlobalKey<FormState>();
final _formData = {};
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: <Widget>[
TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
onSaved: (value) {
_formData['parent'] = value;
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('Parent text: ' + _formData['parent'])));
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SubFormWidget(_formData)),
);
}
},
child: Text('SubForm'),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('Subform text: ' + _formData['child'])));
}
},
child: Text('Submit'),
),
),
],
),
);
}
}
class SubFormWidget extends StatefulWidget {
final Map _formData;
SubFormWidget(this._formData);
@override
SubFormWidgetState createState() {
return SubFormWidgetState();
}
}
class SubFormWidgetState extends State<SubFormWidget> {
final _subFormKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Form(
key: _subFormKey,
child: Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Example'),
backgroundColor: Colors.teal),
body: Column(
children: <Widget>[
Text(widget._formData['parent']),
TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
onSaved: (value) {
widget._formData['child'] = value;
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
onPressed: () {
if (_subFormKey.currentState!.validate()) {
_subFormKey.currentState!.save();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
'Subform text: ' + widget._formData['child'])));
Navigator.pop(context);
}
},
child: Text('Submit'),
),
),
],
),
),
);
}
}
Provider 方法的长示例代码:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<FormData>(
create: (ctx) => FormData(),
),
],
child: MaterialApp(
title: 'Test App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: FormTest2(),
),
);
}
}
class FormTest2 extends StatefulWidget {
FormTest2({Key key}) : super(key: key);
@override
_FormTest2State createState() => _FormTest2State();
}
class _FormTest2State extends State<FormTest2> {
int _selectedIndex = 0;
final List<Widget> _options = [
FormWidget2(),
PlaceholderWidget(Colors.deepOrange),
PlaceholderWidget(Colors.green)
];
void _onItemTap(int index) {
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Example 2'),
backgroundColor: Colors.teal),
body: Center(
child: _options.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
backgroundColor: Colors.teal),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
backgroundColor: Colors.cyan),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'Settings',
backgroundColor: Colors.lightBlue,
),
],
type: BottomNavigationBarType.shifting,
currentIndex: _selectedIndex,
selectedItemColor: Colors.white,
unselectedItemColor: Colors.grey,
iconSize: 40,
onTap: _onItemTap,
elevation: 5),
);
}
}
class PlaceholderWidget extends StatelessWidget {
final Color color;
PlaceholderWidget(this.color);
@override
Widget build(BuildContext context) {
return Container(
color: color,
);
}
}
class FormWidget2 extends StatefulWidget {
@override
FormWidget2State createState() {
return FormWidget2State();
}
}
class FormData extends ChangeNotifier {
var _formData = <String, String>{};
void init() {
_formData = <String, String>{};
// No notifyListeners() needed in this use case
}
void setEntry(String key, String value) {
_formData[key] = value;
// notifyListeners() may or may not be needed in this use case
notifyListeners();
}
String getEntry(String key) {
var value = _formData[key];
return value;
}
}
class FormWidget2State extends State<FormWidget2> {
final _formKey = GlobalKey<FormState>();
@override
void initState() {
Provider.of<FormData>(context, listen: false).init();
super.initState();
}
@override
Widget build(BuildContext context) {
return Consumer<FormData>(
builder: (context, formData, _) => Form(
key: _formKey,
child: Column(
children: <Widget>[
TextFormField(
initialValue: formData.getEntry('parent'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
onSaved: (value) {
formData.setEntry('parent', value);
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
'Parent text: ' + formData.getEntry('parent'))));
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SubFormWidget2()),
);
}
},
child: Text('SubForm'),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
'Subform text: ' + formData.getEntry('child'))));
}
},
child: Text('Submit'),
),
),
],
),
),
);
}
}
class SubFormWidget2 extends StatefulWidget {
@override
SubFormWidget2State createState() {
return SubFormWidget2State();
}
}
class SubFormWidget2State extends State<SubFormWidget2> {
final _subFormKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Form(
key: _subFormKey,
child: Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Example 2'),
backgroundColor: Colors.teal),
body: Consumer<FormData>(
builder: (context, formData, _) => Column(
children: <Widget>[
Text(formData.getEntry('parent')),
TextFormField(
initialValue: formData.getEntry('child'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
onSaved: (value) {
formData.setEntry('child', value);
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
onPressed: () {
if (_subFormKey.currentState.validate()) {
_subFormKey.currentState.save();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
'Subform text: ' + formData.getEntry('child'))));
Navigator.pop(context);
}
},
child: Text('Submit'),
),
),
],
),
),
),
);
}
}
推荐阅读
- powerbi - PowerBi Desktop:创建简单的折线图
- javascript - 取消选中后仍然禁用 - 复选框
- c# - C# windows 窗体,Line 不会绘制!画布未定义
- python - 运行 Python 转换的 exe 时出错:“UnicodeDecodeError: 'charmap' codec can't decode byte” using reverse_geocoder
- django - 如何在 docker-compose 和 Django 中使用 wkHTMLtoPDF
- javascript - 检查无线电输入中的值类型
- mysql - 我应该使用 MD5 32 字符的 MySQL 索引还是包含特殊字符的 ~8 字符索引?
- r - 在 ggplot 中使用计数和累积计数创建 2 个 y 轴
- android - Android 如何使用单个广播接收器创建具有不同请求代码的待处理意图的多个实例
- c - 未知地址上的 SEGV - 查找错误原因