【问题标题】:Passing data between pages with bottom navigation bar in flutter在颤动中使用底部导航栏在页面之间传递数据
【发布时间】:2019-06-12 13:28:45
【问题描述】:

我有一个应用程序在登录后会路由到“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)是否可以提供帮助,但不确定这是否是正确的方法。我也不确定在这种情况下我将如何开始实施它们。

【问题讨论】:

  • 你试过在 StartFunc() 中使用static 吗?
  • @RubensMelo 我尝试将函数设为静态,如果这就是你的意思static StartFunc(),我收到一个错误,提示无法从静态方法访问实例成员,它在 setState 方法和 userData 变量下划线.
  • 是的,如果你想轻松地传递数据,你会想要使用某种 MVVM 模式,例如使用 ScopedModel、BLoC 或创建自己的方法,如果这些对于简单数据来说有点矫枉过正的话.

标签: dart flutter


【解决方案1】:

你的代码有两个问题:

  1. initState() 的主体中使用异步方法 详情见here

  2. 在初始化器中使用实例数据 详情见here

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

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

请注意,我已将实例变量 Data 更改为 data 以符合 Effective Dart 准则。

请注意,要点没有正确解决后端服务与共享偏好的同步问题:这可能必须在最终产品中加以考虑。

我刚刚评论了使您的代码正常工作所必需的内容: 如果您的系统的复杂性和与外部 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) {
    switch (index){
        case 0:
            return PageOne(data:data);
            break;
        case 1:
            return PageTwo(data:data);
            break;
        default:
            return PageOne();
        break;
    }
  }

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

【讨论】:

  • 我已经从你的具体代码开始更新了答案,希望对你有用。
  • 感谢您更新的答案,它非常有启发性,当我尝试在 PageOne 的 initstate 方法中打印“数据”时,它最初似乎为空(猜测 PageTwo 也是如此)为什么不是“数据”就在那里,它不是刚刚传递给 PageOne 构造函数所以它已经可用了吗?
  • 正确:data 最初是null,因为当模拟异步方法getData 中的Future 完成时(1 秒后),它将可用。当getData 返回的future 将以就绪值完成时,传递给getData.then() 的回调将被调用,其值:data 将被更新(并且由于setState,小部件将被重建)
猜你喜欢
  • 2022-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-22
  • 2019-12-15
  • 1970-01-01
  • 1970-01-01
  • 2020-03-07
相关资源
最近更新 更多