简体   繁体   中英

Pass data from one page to another in Flutter

I know there are a ton of questions like these but I'm brand new to Flutter and I haven't found a post that suits my needs or maybe I'm just thinking about it wrong.

I basically have settings.dart and home.dart . Right now, what I'd like to do is based on the checkbox value for either metres _isMetresChecked or feet _isFeetChecked , change the unit on the home screen but don't immediately switch to the home screen, allow the user to continue configuring settings (there are more to add in eventually) and when they go back to the homescreen, it updates. I tried to use the class SettingsData as a way to merge all the configurations and then pass it via Navigator but despite the fact that I was unsuccessful, it wouldn't have let me allow the user to stay on the 'Settings' page.

So how can I pass data between views, without switching views?

home.dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

import 'applicationbar.dart';
import 'navigationdrawer.dart';
import 'bluetooth.dart';


class LuggageFollowerMain extends StatefulWidget {
  @override
  _LuggageFollowerMainState createState() => _LuggageFollowerMainState();
}

class _LuggageFollowerMainState extends State<LuggageFollowerMain> {
  String _string = '0m';  //so if _isFeetChecked is true in settings.dart this is '0ft'


  @override
  Widget build(BuildContext context) {

    return Scaffold(
      backgroundColor: Colors.grey[900],
      appBar: ApplicationBar(title: 'Luggage Follower'),
      drawer: NavigationDrawer(),
      body:Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 10,horizontal: 0),
            child: Text('Luggage Status', style: Theme.of(context).textTheme.body2),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 10,horizontal: 0),
            child: Text('Paired', style: Theme.of(context).textTheme.body1),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 20,horizontal: 0),
            child: Divider(height: 3.0, color: Colors.pinkAccent, indent: 150, endIndent: 150),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 10,horizontal: 0),
            child: Text('Distance to Luggage', style: Theme.of(context).textTheme.body2),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 10,horizontal: 0),
            child: Text(_string, style: Theme.of(context).textTheme.body1), //dynamic text here
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 10,horizontal: 0),
            child: LuggageFollow(),
          ),
        ],
      ),
    );
  }
}

settings.dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:audioplayers/audio_cache.dart';

import 'applicationbar.dart';
import 'navigationdrawer.dart';

class SettingsData {
  bool feet;
  bool metres;

  SettingsData({this.feet, this.metres});
}

class Settings extends StatefulWidget {
  @override
  _SettingsState createState() => _SettingsState();
}

class _SettingsState extends State<Settings> {
  static bool _isFeetChecked   = false;
  static bool _isMetersChecked = true;


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: ApplicationBar(title: 'Settings'),
      drawer: NavigationDrawer(),
      body: Column(
          //crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Padding(
              padding: EdgeInsets.symmetric(vertical: 20.0,horizontal: 20),
              child: Text('Distance Units')
            ),
            Divider(
              height: 3.0,
              color: Colors.pink,
            ),
            Padding(
              padding: EdgeInsets.symmetric(vertical: 20.0,horizontal: 0),
              child: CheckboxListTile(
                title: Text('Distance in feet', style: Theme.of(context).textTheme.body1),
                value: _isFeetChecked,
                onChanged: (bool value) {
                  setState(() {
                    _isFeetChecked = value; _isMetersChecked = !value;});
                },
                checkColor: Colors.white,
                activeColor: Colors.pink,
                subtitle: Text('1 foot ~ 0.3 metres',style: Theme.of(context).textTheme.display2),
              ),
            ),
            Padding(
              padding: EdgeInsets.symmetric(vertical: 20.0,horizontal: 0),
              child: CheckboxListTile(
                title: Text('Distance in metres', style: Theme.of(context).textTheme.body1),
                value: _isMetersChecked,
                onChanged: (bool value) {
                  setState(() { _isMetersChecked = value; _isFeetChecked = !value;updateData();});
                },
                checkColor: Colors.white,
                activeColor: Colors.pink,
                subtitle: Text('1 metre ~ 3 feet',style: Theme.of(context).textTheme.display2),
              ),
            ),
            Padding(
              padding: EdgeInsets.symmetric(vertical: 30.0,horizontal: 20),
              child: Text('Notifications')
            ),
            Divider(
                height: 3.0,
                color: Colors.pink,
            ),

              ),
            ),
          ],
      ),
    );
  }
}

I know there are a ton of questions like these but I'm brand new to Flutter and I haven't found a post that suits my needs or maybe I'm just thinking about it wrong.

I basically have settings.dart and home.dart . Right now, what I'd like to do is based on the checkbox value for either metres _isMetresChecked or feet _isFeetChecked , change the unit on the home screen but don't immediately switch to the home screen, allow the user to continue configuring settings (there are more to add in eventually) and when they go back to the homescreen, it updates. I tried to use the class SettingsData as a way to merge all the configurations and then pass it via Navigator but despite the fact that I was unsuccessful, it wouldn't have let me allow the user to stay on the 'Settings' page.

So how can I pass data between views, without switching views?

home.dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

import 'applicationbar.dart';
import 'navigationdrawer.dart';
import 'bluetooth.dart';


class LuggageFollowerMain extends StatefulWidget {
  @override
  _LuggageFollowerMainState createState() => _LuggageFollowerMainState();
}

class _LuggageFollowerMainState extends State<LuggageFollowerMain> {
  String _string = '0m';  //so if _isFeetChecked is true in settings.dart this is '0ft'


  @override
  Widget build(BuildContext context) {

    return Scaffold(
      backgroundColor: Colors.grey[900],
      appBar: ApplicationBar(title: 'Luggage Follower'),
      drawer: NavigationDrawer(),
      body:Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 10,horizontal: 0),
            child: Text('Luggage Status', style: Theme.of(context).textTheme.body2),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 10,horizontal: 0),
            child: Text('Paired', style: Theme.of(context).textTheme.body1),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 20,horizontal: 0),
            child: Divider(height: 3.0, color: Colors.pinkAccent, indent: 150, endIndent: 150),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 10,horizontal: 0),
            child: Text('Distance to Luggage', style: Theme.of(context).textTheme.body2),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 10,horizontal: 0),
            child: Text(_string, style: Theme.of(context).textTheme.body1), //dynamic text here
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 10,horizontal: 0),
            child: LuggageFollow(),
          ),
        ],
      ),
    );
  }
}

settings.dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:audioplayers/audio_cache.dart';

import 'applicationbar.dart';
import 'navigationdrawer.dart';

class SettingsData {
  bool feet;
  bool metres;

  SettingsData({this.feet, this.metres});
}

class Settings extends StatefulWidget {
  @override
  _SettingsState createState() => _SettingsState();
}

class _SettingsState extends State<Settings> {
  static bool _isFeetChecked   = false;
  static bool _isMetersChecked = true;


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: ApplicationBar(title: 'Settings'),
      drawer: NavigationDrawer(),
      body: Column(
          //crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Padding(
              padding: EdgeInsets.symmetric(vertical: 20.0,horizontal: 20),
              child: Text('Distance Units')
            ),
            Divider(
              height: 3.0,
              color: Colors.pink,
            ),
            Padding(
              padding: EdgeInsets.symmetric(vertical: 20.0,horizontal: 0),
              child: CheckboxListTile(
                title: Text('Distance in feet', style: Theme.of(context).textTheme.body1),
                value: _isFeetChecked,
                onChanged: (bool value) {
                  setState(() {
                    _isFeetChecked = value; _isMetersChecked = !value;});
                },
                checkColor: Colors.white,
                activeColor: Colors.pink,
                subtitle: Text('1 foot ~ 0.3 metres',style: Theme.of(context).textTheme.display2),
              ),
            ),
            Padding(
              padding: EdgeInsets.symmetric(vertical: 20.0,horizontal: 0),
              child: CheckboxListTile(
                title: Text('Distance in metres', style: Theme.of(context).textTheme.body1),
                value: _isMetersChecked,
                onChanged: (bool value) {
                  setState(() { _isMetersChecked = value; _isFeetChecked = !value;updateData();});
                },
                checkColor: Colors.white,
                activeColor: Colors.pink,
                subtitle: Text('1 metre ~ 3 feet',style: Theme.of(context).textTheme.display2),
              ),
            ),
            Padding(
              padding: EdgeInsets.symmetric(vertical: 30.0,horizontal: 20),
              child: Text('Notifications')
            ),
            Divider(
                height: 3.0,
                color: Colors.pink,
            ),

              ),
            ),
          ],
      ),
    );
  }
}

So first of all you only need 1 bool for your measurements. It's either feet or meters. So that simplifies things a bit. Here is a example using a callback function to set the state change in the parent widget. This is just one way of many this can be done and there may be other ways you may like better.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: LuggageFollowerMain(),
    );
  }
}


class Settings extends StatefulWidget {

  //Static references to our parent widget varibles
  final bool isM;
  final changeMeeters;

  Settings(bool isM, changeMeeters(bool meeters)):
  this.isM = isM,
  this.changeMeeters = changeMeeters
  ;

  @override
  _SettingsState createState() => _SettingsState();
}

class _SettingsState extends State<Settings> {
  @override
  void initState() { 
    super.initState();
    //set our varible state to be that of the parent screen
    //isMeeters is a local var to Settings and is changeable. 
    //isM is a static and can't be changed. isM was passed in 
    //from our home screen. We need a changeable var or the 
    //checkboxes won't update correctly. Thats why we need 
    //isMeeters (lol bad spelling). When the settings screen 
    //first loads we use initState to take the value of 
    //isM(from our home screen) and copy it into isMeeters 
    //as it's initial value before it is built and shown 
    //to the user for the first time. 
    isMeeters = widget.isM;
  }

  //our varible to use for our check boxes
  var isMeeters;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Column(
        children: <Widget>[
          Padding(
              padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 20),
              child: Text('Distance Units')),
          Divider(
            height: 3.0,
            color: Colors.pink,
          ),
          Padding(
            padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 0),
            child: CheckboxListTile(
              title: Text('Distance in feet',
                  style: Theme.of(context).textTheme.body1),
              //use the local var change when tapped
              value: isMeeters,
              onChanged: (bool value) {
                setState(() {
                  isMeeters = true;
                });
                //call back to the parent widget so it can be updated
                widget.changeMeeters(true);
              },
              checkColor: Colors.white,
              activeColor: Colors.pink,
              subtitle: Text('1 foot ~ 0.3 metres',
                  style: Theme.of(context).textTheme.display2),
            ),
          ),
          Padding(
            padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 0),
            child: CheckboxListTile(
              title: Text('Distance in metres',
                  style: Theme.of(context).textTheme.body1),
              //use the local var change when tapped. Use ! to invert the bool. This way only one is needed.
              value: !isMeeters,
              onChanged: (bool value) {
                setState(() {
                  isMeeters = false;
                });
                //call back to the parent widget so it can be updated
                widget.changeMeeters(false);
              },
              checkColor: Colors.white,
              activeColor: Colors.pink,
              subtitle: Text('1 metre ~ 3 feet',
                  style: Theme.of(context).textTheme.display2),
            ),
          ),
          Padding(
              padding: EdgeInsets.symmetric(vertical: 30.0, horizontal: 20),
              child: Text('Notifications')),
          Divider(
            height: 3.0,
            color: Colors.pink,
          ),
        ],
      ),
    );
  }
}

class LuggageFollowerMain extends StatefulWidget {
  @override
  _LuggageFollowerMainState createState() => _LuggageFollowerMainState();
}

class _LuggageFollowerMainState extends State<LuggageFollowerMain> {
      //store the distance as a number not a string
      double distance = 0.0;
      //store the measurement type setting
      bool isMeeters = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => Settings(isMeeters, (newMeeters){
              //when we get our callback from settings we set the change to the home screen var here
              setState(() {
                isMeeters = newMeeters;
              });

            })),
          );
        },
      ),
      backgroundColor: Colors.grey[900],
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 0),
            child: Text('Luggage Status',
                style: Theme.of(context).textTheme.body2),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 0),
            child: Text('Paired', style: Theme.of(context).textTheme.body1),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 0),
            child: Divider(
                height: 3.0,
                color: Colors.pinkAccent,
                indent: 150,
                endIndent: 150),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 0),
            child: Text('Distance to Luggage',
                style: Theme.of(context).textTheme.body2),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 0),
            //use the new number var and change it's suffix m or f depending on what our setting is.
            child: Text(distance.toString() + (isMeeters ? "m" : "f"),
                style: Theme.of(context).textTheme.body1), //dynamic text here
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 0),
          ),
        ],
      ),
    );
  }
}

To pass data from one page to another, you can make use of passing arguments to a named route .

Below is a minimal example of passing a boolean to and from the Settings page by way of arguments.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
      routes: {
        "home": (BuildContext homeContext) => HomePage(),
        "settings": (BuildContext settingsContext) => SettingsPage(),
      },
      initialRoute: "home",
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  bool _isMetersAccordingToHomePage = false;

  void _updateIsMetersAccordingToHomePage(bool _value) {
    setState(() {
      _isMetersAccordingToHomePage = _value ?? _isMetersAccordingToHomePage;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Home Page")),
      body: Column(
        children: <Widget>[
          Text("Is Displayed in meters? $_isMetersAccordingToHomePage"),
          RaisedButton(
            onPressed: () {
              // This is the important part
              Navigator.of(context)
                  .pushNamed(
                "settings",
                // we are passing a value to the settings page
                arguments: _isMetersAccordingToHomePage,
              )
                  // and then, when we `pop` from the settings page,
                  // we will get back an updated value
                  .then(
                (_isMetersAccordingToSettingsPage) {
                  _updateIsMetersAccordingToHomePage(
                      _isMetersAccordingToSettingsPage);
                },
              );
            },
            child: Text("Go to Settings Page"),
          ),
        ],
      ),
    );
  }
}

class SettingsPage extends StatefulWidget {
  @override
  _SettingsPageState createState() => _SettingsPageState();
}

class _SettingsPageState extends State<SettingsPage> {
  bool _isMetersAccordingToSettingsPage;

  void _updateIsMetersAccordingToSettingsPage(bool _value) {
    setState(() {
      _isMetersAccordingToSettingsPage = _value;
    });
  }

  @override
  Widget build(BuildContext context) {
    bool _isMetersAccordingToHomePage =
        ModalRoute.of(context).settings.arguments;

    return Scaffold(
      appBar: AppBar(
        title: Text("Setting Page"),
        leading: IconButton(
          icon: Icon(Icons.arrow_back_ios),
          onPressed: () {
            Navigator.of(context).pop<bool>(_isMetersAccordingToSettingsPage);
          },
        ),
      ),
      body: Column(
        children: <Widget>[
          CheckboxListTile(
            value: _isMetersAccordingToSettingsPage ??
                _isMetersAccordingToHomePage ??
                false,
            onChanged: _updateIsMetersAccordingToSettingsPage,
            title: Text("Display in meters?"),
          ),
        ],
      ),
    );
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM