一尘不染

带有底部导航栏的页面之间的数据传递

flutter

我有一个应用程序,该应用程序在登录后会路由到“
mainapp”页面。该应用程序包含一个底部导航栏,该导航栏显示相应的已按下图标的页面。我想将类型的数据传递Map<String, dynamic>给这些页面,但是遇到了麻烦。此映射是通过一个函数生成的,该函数从服务器获取数据,将其保存到共享首选项,然后加载共享首选项并将其作为映射返回(全部包含在中getData())。我想传递此地图,这样就不必每次都加载共享的首选项,但也会在需要时与共享的首选项一起更新此地图(可能在其中一个页面上执行操作)。

class MainApp extends StatefulWidget {
  @override
  _MainAppState createState() => _MainAppState();
}

class _MainAppState extends State<MainApp> {


  Map<String, dynamic> Data;


  StartFunc() async {
   Data = await getData();
   setState(() {}); 
 }


  @override
  void initState() {
    StartFunc();
    super.initState();
  }

  var _pages = [
    PageOne(Data:Data),
    PageTwo(),
    PageThree(),
   PageFour(),
   PageFive(),
  ];

  int _currentIndex = 0;

  onTabTapped(int index) {
    setState(() {
      _currentIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return _currentIndex == 2
        ? PageTwo()
        : Scaffold(
      body: _pages[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        type: BottomNavigationBarType.fixed,
        items: [
          BottomNavigationBarItem(
              icon: Icon(Icons.library_books), title: Text('')),
          BottomNavigationBarItem(
              icon: Icon(Icons.notifications), title: Text('')),
          BottomNavigationBarItem(
              icon: Icon(Icons.add_circle_outline), title: Text('')),
          BottomNavigationBarItem(
              icon: Icon(Icons.mail), title: Text('')),
          BottomNavigationBarItem(
              icon: Icon(Icons.person), title: Text('')),
        ],
        onTap: onTabTapped,
        currentIndex: _currentIndex,
      ),
    );
  }
}

我收到一个错误消息,指出 只能在初始化程序中访问静态成员
。我想知道继承的窗口小部件或其他设计模式(例如范围模型和BLoC)是否可以帮助您,但不确定这是否是正确的方法。我也不确定在这种情况下如何开始实施它们。


阅读 346

收藏
2020-08-13

共1个答案

一尘不染

您的代码中有两个问题:

  1. 在体内使用异步方法initState()这里了解详细信息

  2. 在初始化程序中使用实例数据,请参见此处以获取详细信息

接下来的工作是对您的代码进行非常基本的重写,并进行了最少的更正。

数据映射从模拟的后端加载,在内部更新PageOne并在PageTwoonTap回调中打印到控制台。

请注意,我已经改变了实例变量Data,以data使其符合有效飞镖准则。

请注意,要点无法正确地解决具有共享首选项的后端服务的同步:这可能是最终产品中必须考虑的问题。

我只是评论了使代码正常工作的必要条件:如果系统的复杂性以及与外部API的关系开始增长,那么可能值得考虑使用Bloc架构。

import 'package:flutter/material.dart';

void main() => runApp(new MainApp());

// Mock up of an async backend service
Future<Map<String, dynamic>> getData() async {
  return Future.delayed(Duration(seconds: 1), () => {'prop1': 'value1'});
}

class PageOne extends StatelessWidget {
  final Map<String, dynamic> data;

  PageOne({Key key, this.data}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: RaisedButton(
        child: const Text('update preferences'),
        onPressed: () {
          data['prop2'] = 'value2';
        },
      ),
    );
  }
}

class PageTwo extends StatelessWidget {

  final Map<String, dynamic> data;

  PageTwo({Key key, this.data}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: RaisedButton(
        child: const Text('Got It!'),
        onPressed: () {
          print("data is now: [$data]");
        },
      ),
    );
  }
}

class MainApp extends StatefulWidget {
  @override
  _MainAppState createState() => _MainAppState();
}

class _MainAppState extends State<MainApp> {
  //Map<String, dynamic> Data;
  Map<String, dynamic> data;

  /*
  StartFunc() async {
    Data = await getData();
    setState(() {});
  }
  */

  @override
  void initState() {
    //StartFunc();
    super.initState();
    getData().then((values) {
      setState(() {
        data = values;
      });
    });
  }

  /*
  PageOne(data:data) is an invalid value for an initializer:
   there is no way to access this at this point.
    Initializers are executed before the constructor,
    but this is only allowed to be accessed after the call
    to the super constructor.

  */
  /*
  var _pages = [
    PageOne(data:data),
    PageTwo(),
  ];
  */

  Widget getPage(int index) {
    if (index == 0) {
      return PageOne(data:data);
    }
    if (index == 1) {
      return PageTwo(data:data);
    }
    // A fallback, in this case just PageOne
    return PageOne();
  }

  int _currentIndex = 0;

  onTabTapped(int index) {
    setState(() {
      _currentIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    /*
    return _currentIndex == 2
        ? PageTwo()
        : Scaffold(

    I use a MaterialApp because of material widgets (RaisedButton)
    It is not mandatory, but it is mainstream in flutter

     */
    return MaterialApp(
        title: 'My App',
        home: Scaffold(
          appBar: AppBar(title: Text("My App Bar")),
          body: getPage(_currentIndex),
          bottomNavigationBar: BottomNavigationBar(
            type: BottomNavigationBarType.fixed,
            items: [
              BottomNavigationBarItem(
                  icon: Icon(Icons.first_page), title: Text('')),
              BottomNavigationBarItem(
                  icon: Icon(Icons.last_page), title: Text('')),
            ],
            onTap: onTabTapped,
            currentIndex: _currentIndex,
          ),
        ));
  }
}
2020-08-13