首页 > 解决方案 > 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();
  }
}

标签: flutterdartprovider

解决方案


我不确定我是否从上面的代码片段中完全理解你在做什么,但我想我理解这个要求:

  • 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'),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

推荐阅读