Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing data between pages with bottom navigation bar in flutter

Tags:

flutter

dart

I have an app that routes to a "mainapp" page after logging in. This app contains a bottom navigation bar which displays pages of the corresponding pressed icon. I want to pass data of type Map<String, dynamic> to these pages but I am having trouble. This map is generated from a function that fetches the data from a server, saves it to shared preferences, then loads the shared preferences and returns it as a map (all contained in getData()). I want to pass this map around so I don't have to load shared preferences each time, but will also update this map along with shared preferences when needed( possibly an action on one of the pages).

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

I'm getting an error saying Only static members can be accessed in initializers. I was wondering if inherited widgets or other design patterns such as scoped model and BLoC can help but not sure if that's the right way to go. I'm also not sure how I would start implementing them in this case.

like image 949
DanT29 Avatar asked Jan 18 '19 09:01

DanT29


1 Answers

There are two problems in your code:

  1. using an async method in the body of initState() see here for details

  2. using instance data in an initializer see here for details

What follow is a very basic rewrite of your code, with minimal corrections.

The data map is loaded from a mocked backend, updated inside PageOne and printed to console in PageTwo onTap callback.

Please note that I've changed instance variable Data to data to be compliant with Effective Dart guidelines.

Note that the gist does not properly addresses the synchronization of the backend service with the shared preferences: this is something that have probably to be accounted in the final product.

I just commented what it is necessary to get your code works: if the complexity of your system and the relations with external API start growing it could be worth considering a Bloc architecture.

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,
          ),
        ));
  }
}
like image 157
attdona Avatar answered Nov 18 '22 22:11

attdona