preloader
軟體工程

Pass arguments between Flutter app screens by Navigator.pushName()

Suppose your app, which built by flutter 1.2, has ItemListScreen and ItemDetailScreen, and this app uses named route to manage routing of app. Users can click one of item in ItemListScreen and then ItemDetailScreen will show detail information of the clicked item. You can find following example to decouple between screens by no need importing ItemDetailScreen class to ItemListScreen file. Solution: Use

ModalRoute.of(context).settings.arguments

in your routes.dart file to pass arguments to target screen.

 

 

Tested Environment:

  1. Flutter 1.2

  2. I set ItemListScreen and ItemDetailScreen are StatefulWidget. I think there is another way to achieve same effect.

  3. Dart 2.2

 

Let’s start! Keep on eyes on “KEYPOINT of this article”.

You have following file structure:

  1. PROJECT_FOLDER/lib/main.dart

  2. PROJECT_FOLDER/lib/route.dart

  3. PROJECT_FOLDER/lib/screens/Item/ItemListScreen.dart

  4. PROJECT_FOLDER/lib/screens/Item/ItemDetailScreen.dart

  5. PROJECT_FOLDER/lib/screens/Item/index.dart

  6. PROJECT_FOLDER/lib/models/Item.dart

 

Content of main.dart

import 'routes.dart';
void main() {
  new Routes();
}

 

Content of routes.dart

import 'package:flutter/material.dart';
import 'package:my_appproject/screens/Item/index.dart';
class Routes {
  final routes = {
    '/': (BuildContext context) => new ItemListScreen(),
    '/Item/Detail': (BuildContext context) => new ItemDetailScreen(
      item: ModalRoute.of(context).settings.arguments
      ), // KEYPOINT of this article
  };
    
  Routes() {
    runApp(new MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.orange,
        textTheme: TextTheme(
          body1: TextStyle(
            fontSize: 24,
            fontWeight: FontWeight.bold
            ),// TextStyle
          ), // TextTheme
        ),//
      ThemeDataroutes: routes,
      )// materialapp
    );
  }
}

 

 

Content of ItemListScreen.dart: 


import 'package:flutter/material.dart';
import 'package:my\_appproject/models/Item.dart';
class ItemListScreen extends StatefulWidget {
  @overrideState<StatefulWidget> createState() {
    return new ItemListState();
  }
}

class ItemListState extends State<ItemListScreen> {
  var _itemList = [
    {
      "name": "AA",
      "price": 10,
      "qty": 2
    },
    {
      "name": "BB",
      "price": 5,
      "qty": 3
    },
    {
      "name": "CC",
      "price": 7,
      "qty": 7
    },
  ];
  
  List<Item> _uiItemList;
  @overridevoid initState() {
    _uiItemList = _itemList.map<Item>((Map \_item) => Item(
      name: _item['name'], 
      price: _item['price'],
      qty: _item['qty']
      )
    ).toList();
    super.initState();
  }
  
  @overrideWidget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("shelf items")),
        body: ListView.separated(
          separatorBuilder: (BuildContext context, int index) => 
          Divider(color: Colors.black),
          padding: EdgeInsets.all(8.0),
          itemCount: _uiItemList.length,
          itemBuilder: (BuildContext context, int index) {
            return GestureDetector(child: Row(
              children: <Widget> [
                Expanded(
                  child: Text(_uiItemList[index].name),
                  flex: 2,
                ), // Expanded
                Expanded(
                  child: Text(_uiItemList[index].price.toString()),
                  flex: 1,
                ),// Expanded
                Expanded(
                  child: Text(_uiItemList[index].qty.toString()),
                  flex: 1,
                ),// Expanded
              ],// children
            ), // Row
          onTap: () {
            Navigator.pushNamed(context, '/Item/Detail', arguments: _uiItemList[index]); // KEYPOINT of this article
            }
          );// GestureDetector
        }, // itemBuilder
      ),// ListView.separated
    ); // Scaffold
  }
}

 

 

Content of ItemDetailScreen:

import 'package:flutter/material.dart';
import 'package:my_appproject/models/Item.dart';

class ItemDetailScreen extends StatefulWidget {
  final Item item;
  
  ItemDetailScreen({
    Key key,
    this.item
  }) : super(key: key);
  
  @overrideItemDetailState createState() => ItemDetailState();
}
  
class ItemDetailState extends State<ItemDetailScreen> {
  String _displayedText;
  
  @overrideItemDetailScreen get widget => super.widget;// KEYPOINT of this article
  
  @overridevoid initState() { // KEYPOINT of this article
  
    if (widget.item == null) { // if pass no argument to this route, we display empty string
      _displayedText = "";
    } else { // if pass the item argument to this route, we display the item detail
      _displayedText = widget.item.name + "/" + widget.item.price.toString() + "/" + widget.item.qty.toString();
    }
    
    super.initState();
    
  }
  
  @overrideWidget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("the Item detail")
      ),
      body: Center(
        child: Text(_displayedText),
      ), // Center
    );// Scaffold
  }
}

 

 

Content of screens/Item/index.dart:

export 'package:my_appproject/screens/Item/ItemListScreen.dart';
export 'package:my_appproject/screens/Item/ItemDetailScreen.dart';

 

Content of models/Item.dart:

class Item {
  String name;
  num price;
  int qty;
  Item({
    this.name, this.price, this.qty
  });
}

 

You can find my example code on GitHub repository.

reference link:

  1. hhk’s comment at https://stackoverflow.com/a/50289032/2704360  Passing data to StatefulWidget and accessing it in it’s state in Flutter

2. goderbauer’s PR based on Hixie’s one:  https://github.com/flutter/flutter/pull/27058#issue-247522839