带有 return 数据的 Flutter Back 按钮
Flutter Back button with return data
我有一个界面,其中有两个弹出按钮和 return true 或 false,如下所示:
onPressed: () => Navigator.pop(context, false)
我需要调整应用栏中的后退按钮,所以它弹出并且 return 为 false。有办法实现吗?
使用下面的代码从您的 activity.
中获取结果
Future _startActivity() async {
Map results = await Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context){
return new StartActivityForResult();
}));
if (results != null && results.containsKey('item')) {
setState(() {
stringFromActivity = results['item'];
print(stringFromActivity);
});
}
}
完整的源代码
import 'package:flutter/material.dart';
import 'activity_for_result.dart';
import 'dart:async';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Start Activity For Result'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String stringFromActivity = 'Start Activity To Change Me \n';
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
stringFromActivity, style: new TextStyle(fontSize: 20.0), textAlign: TextAlign.center,
),
new Container(height: 20.0,),
new RaisedButton(child: new Text('Start Activity'),
onPressed: () => _startActivity(),)
],
),
),
);
}
Future _startActivity() async {
Map results = await Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context){
return new StartActivityForResult();
}));
if (results != null && results.containsKey('item')) {
setState(() {
stringFromActivity = results['item'];
print(stringFromActivity);
});
}
}
}
import 'package:flutter/material.dart';
class StartActivityForResult extends StatelessWidget{
List<String>list = ['','','','','','','','','',];
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text('Selecte Smily'),
),
body: new ListView.builder(itemBuilder: (context, i){
return new ListTile(title: new Text(list[i]),
onTap: (){
Navigator.of(context).pop({'item': list[i]});
},
);
}, itemCount: list.length,),
);
}
}
获取完整的 运行 如何工作的示例来自
here
首先,删除自动附加的后退按钮(参见)
然后,创建您自己的后退按钮。像这样:
IconButton(
onPressed: () => Navigator.pop(context, false),
icon: Icon(Icons.arrow_back),
)
这可能对您有所帮助
第一屏
void goToSecondScreen()async {
var result = await Navigator.push(_context, new MaterialPageRoute(
builder: (BuildContext context) => new SecondScreen(context),
fullscreenDialog: true,)
);
Scaffold.of(_context).showSnackBar(SnackBar(content: Text("$result"),duration: Duration(seconds: 3),));
}
第二屏
Navigator.pop(context, "Hello world");
默认 BackButton
接管 AppBar
的前导 属性,因此您需要做的就是用您的 leading
属性 覆盖自定义后退按钮,例如:
leading: IconButton(
icon: Icon(Icons.chevron_left),
onPressed: () => Navigator.pop(context, false),
),
虽然您可以覆盖自定义行为的后退按钮,但不要这样做。
您应该处理 null 场景,而不是用自定义弹出窗口覆盖按钮。
您不想手动覆盖图标的原因有几个:
- 图标在 IOS 和 Android 上发生变化。在 IOS 上它使用
arrow_back_ios
而 android 使用 arrow_back
- 如果没有返回路径,图标可能会自动消失
- 物理后退按钮仍将 return
null
。
而是应执行以下操作:
var result = await Navigator.pushNamed<bool>(context, "/");
if (result == null) {
result = false;
}
实现此目的的最简单方法是:
在您的 body 中,将 WillPopScope
作为 parent 小部件
并在 onWillPop : () {}
调用
Navigator.pop(context, false);
onWillPop
of WillPopScope
将在您按下 AppBar 上的后退按钮时自动触发
您可以将 data/arguments 从一个屏幕传递到另一个屏幕,
考虑这个例子:
screen1.dart:
import 'package:flutter/material.dart';
import 'screen2.dart';
class Screen1 extends StatelessWidget {
Screen1(this.indx);
final int indx;
@override
Widget build(BuildContext context) {
return new S1(indx: indx,);
}
}
class S1 extends StatefulWidget {
S1({Key key, this.indx}) : super(key: key);
final int indx;
@override
S1State createState() => new S1State(indx);
}
class S1State extends State<VD> {
int indx = 5;
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
leading: new IconButton(icon: const Icon(Icons.iconName), onPressed: () {
Navigator.pushReplacement(context, new MaterialPageRoute(
builder: (BuildContext context) => new Screen2(indx),
));
}),
),
);
}
}
屏幕 2:
import 'package:flutter/material.dart';
import 'screen2.dart';
class Screen2 extends StatelessWidget {
Screen2(this.indx);
final int indx;
@override
Widget build(BuildContext context) {
return new S2(indx: indx,);
}
}
class S2 extends StatefulWidget {
S2({Key key, this.indx}) : super(key: key);
final int indx;
@override
S2State createState() => new S2State(indx);
}
class S2State extends State<VD> {
int indx = 1;
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
leading: new IconButton(icon: const Icon(Icons.Icons.arrow_back), onPressed: () {
Navigator.pushReplacement(context, new MaterialPageRoute(
builder: (BuildContext context) => new Screen1(indx),
));
}),
),
);
}
}
要在屏幕之间传递数据,请将 argument/data 传递给 Navigator.pushReplacement()
中的屏幕构造函数。您可以根据需要传递任意数量的参数。
这一行
Navigator.pushReplacement(context, new MaterialPageRoute(
builder: (BuildContext context) => new Screen1(indx),
));
将转到 Screen1 并调用 Screen1 的 initState 和 build 方法,以便您可以获得更新的值。
要在导航中弹出数据并传回数据,您需要使用屏幕 1 中的 .then()
。下面是示例。
屏幕 2:
class DetailsClassWhichYouWantToPop {
final String date;
final String amount;
DetailsClassWhichYouWantToPop(this.date, this.amount);
}
void getDataAndPop() {
DetailsClassWhichYouWantToPop detailsClass = new DetailsClassWhichYouWantToPop(dateController.text, amountController.text);
Navigator.pop(context, detailsClass); //pop happens here
}
new RaisedButton(
child: new Text("Edit"),
color: UIData.col_button_orange,
textColor: Colors.white,
onPressed: getDataAndPop, //calling pop here
),
屏幕 1:
class Screen1 extends StatefulWidget {
//var objectFromEditBill;
DetailsClassWhichYouWantToPop detailsClass;
MyBills({Key key, this.detailsClass}) : super(key: key);
@override
Screen1State createState() => new Screen1State();
}
class Screen1State extends State<Screen1> with TickerProviderStateMixin {
void getDataFromEdit(DetailsClassWhichYouWantToPop detailClass) {
print("natureOfExpense Value:::::: " + detailClass.date);
print("receiptNumber value::::::: " + detailClass.amount);
}
void getDataFromEdit(DetailsClassWhichYouWantToPop detailClass) {
print("natureOfExpense Value:::::: " + detailClass.natureOfExpense);
print("receiptNumber value::::::: " + detailClass.receiptNumber);
}
void pushFilePath(File file) async {
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Screen2(fileObj: file),
),
).then((val){
getDataFromScreen2(val); //you get details from screen2 here
});
}
}
更简单的方法是将正文包裹在 WillPopScope 中,这样它将与顶部的 后退按钮一起工作 并且Android 底部的后退按钮。
这是一个示例,其中两个后退按钮 return false:
final return = Navigator.of(context).push(MaterialPageRoute<bool>(
builder: (BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("New Page"),
),
body: WillPopScope(
onWillPop: () async {
Navigator.pop(context, false);
return false;
},
child: newPageStuff(),
),
);
},
));
在他们建议使用的其他答案中:
leading: BackButton(...)
我发现这适用于顶部的后退按钮,而不适用于 Android 按钮。
无论如何我都包括一个例子:
final return = Navigator.of(context).push(MaterialPageRoute<bool>(
builder: (BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: BackButton(
onPressed: () => Navigator.pop(context, false),
),
title: Text("New Page"),
),
body: newPageStuff(),
);
},
));
试试这个:
void _onBackPressed() {
// Called when the user either presses the back arrow in the AppBar or
// the dedicated back button.
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {
_onBackPressed();
return Future.value(false);
},
child: Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: _onBackPressed,
),
),
),
);
}
我有一个界面,其中有两个弹出按钮和 return true 或 false,如下所示:
onPressed: () => Navigator.pop(context, false)
我需要调整应用栏中的后退按钮,所以它弹出并且 return 为 false。有办法实现吗?
使用下面的代码从您的 activity.
中获取结果Future _startActivity() async {
Map results = await Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context){
return new StartActivityForResult();
}));
if (results != null && results.containsKey('item')) {
setState(() {
stringFromActivity = results['item'];
print(stringFromActivity);
});
}
}
完整的源代码
import 'package:flutter/material.dart';
import 'activity_for_result.dart';
import 'dart:async';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Start Activity For Result'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String stringFromActivity = 'Start Activity To Change Me \n';
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
stringFromActivity, style: new TextStyle(fontSize: 20.0), textAlign: TextAlign.center,
),
new Container(height: 20.0,),
new RaisedButton(child: new Text('Start Activity'),
onPressed: () => _startActivity(),)
],
),
),
);
}
Future _startActivity() async {
Map results = await Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context){
return new StartActivityForResult();
}));
if (results != null && results.containsKey('item')) {
setState(() {
stringFromActivity = results['item'];
print(stringFromActivity);
});
}
}
}
import 'package:flutter/material.dart';
class StartActivityForResult extends StatelessWidget{
List<String>list = ['','','','','','','','','',];
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text('Selecte Smily'),
),
body: new ListView.builder(itemBuilder: (context, i){
return new ListTile(title: new Text(list[i]),
onTap: (){
Navigator.of(context).pop({'item': list[i]});
},
);
}, itemCount: list.length,),
);
}
}
获取完整的 运行 如何工作的示例来自 here
首先,删除自动附加的后退按钮(参见
然后,创建您自己的后退按钮。像这样:
IconButton(
onPressed: () => Navigator.pop(context, false),
icon: Icon(Icons.arrow_back),
)
这可能对您有所帮助
第一屏
void goToSecondScreen()async {
var result = await Navigator.push(_context, new MaterialPageRoute(
builder: (BuildContext context) => new SecondScreen(context),
fullscreenDialog: true,)
);
Scaffold.of(_context).showSnackBar(SnackBar(content: Text("$result"),duration: Duration(seconds: 3),));
}
第二屏
Navigator.pop(context, "Hello world");
默认 BackButton
接管 AppBar
的前导 属性,因此您需要做的就是用您的 leading
属性 覆盖自定义后退按钮,例如:
leading: IconButton(
icon: Icon(Icons.chevron_left),
onPressed: () => Navigator.pop(context, false),
),
虽然您可以覆盖自定义行为的后退按钮,但不要这样做。
您应该处理 null 场景,而不是用自定义弹出窗口覆盖按钮。 您不想手动覆盖图标的原因有几个:
- 图标在 IOS 和 Android 上发生变化。在 IOS 上它使用
arrow_back_ios
而 android 使用arrow_back
- 如果没有返回路径,图标可能会自动消失
- 物理后退按钮仍将 return
null
。
而是应执行以下操作:
var result = await Navigator.pushNamed<bool>(context, "/");
if (result == null) {
result = false;
}
实现此目的的最简单方法是:
在您的 body 中,将 WillPopScope
作为 parent 小部件
并在 onWillPop : () {}
调用
Navigator.pop(context, false);
onWillPop
of WillPopScope
将在您按下 AppBar 上的后退按钮时自动触发
您可以将 data/arguments 从一个屏幕传递到另一个屏幕,
考虑这个例子:
screen1.dart:
import 'package:flutter/material.dart';
import 'screen2.dart';
class Screen1 extends StatelessWidget {
Screen1(this.indx);
final int indx;
@override
Widget build(BuildContext context) {
return new S1(indx: indx,);
}
}
class S1 extends StatefulWidget {
S1({Key key, this.indx}) : super(key: key);
final int indx;
@override
S1State createState() => new S1State(indx);
}
class S1State extends State<VD> {
int indx = 5;
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
leading: new IconButton(icon: const Icon(Icons.iconName), onPressed: () {
Navigator.pushReplacement(context, new MaterialPageRoute(
builder: (BuildContext context) => new Screen2(indx),
));
}),
),
);
}
}
屏幕 2:
import 'package:flutter/material.dart';
import 'screen2.dart';
class Screen2 extends StatelessWidget {
Screen2(this.indx);
final int indx;
@override
Widget build(BuildContext context) {
return new S2(indx: indx,);
}
}
class S2 extends StatefulWidget {
S2({Key key, this.indx}) : super(key: key);
final int indx;
@override
S2State createState() => new S2State(indx);
}
class S2State extends State<VD> {
int indx = 1;
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
leading: new IconButton(icon: const Icon(Icons.Icons.arrow_back), onPressed: () {
Navigator.pushReplacement(context, new MaterialPageRoute(
builder: (BuildContext context) => new Screen1(indx),
));
}),
),
);
}
}
要在屏幕之间传递数据,请将 argument/data 传递给 Navigator.pushReplacement()
中的屏幕构造函数。您可以根据需要传递任意数量的参数。
这一行
Navigator.pushReplacement(context, new MaterialPageRoute(
builder: (BuildContext context) => new Screen1(indx),
));
将转到 Screen1 并调用 Screen1 的 initState 和 build 方法,以便您可以获得更新的值。
要在导航中弹出数据并传回数据,您需要使用屏幕 1 中的 .then()
。下面是示例。
屏幕 2:
class DetailsClassWhichYouWantToPop {
final String date;
final String amount;
DetailsClassWhichYouWantToPop(this.date, this.amount);
}
void getDataAndPop() {
DetailsClassWhichYouWantToPop detailsClass = new DetailsClassWhichYouWantToPop(dateController.text, amountController.text);
Navigator.pop(context, detailsClass); //pop happens here
}
new RaisedButton(
child: new Text("Edit"),
color: UIData.col_button_orange,
textColor: Colors.white,
onPressed: getDataAndPop, //calling pop here
),
屏幕 1:
class Screen1 extends StatefulWidget {
//var objectFromEditBill;
DetailsClassWhichYouWantToPop detailsClass;
MyBills({Key key, this.detailsClass}) : super(key: key);
@override
Screen1State createState() => new Screen1State();
}
class Screen1State extends State<Screen1> with TickerProviderStateMixin {
void getDataFromEdit(DetailsClassWhichYouWantToPop detailClass) {
print("natureOfExpense Value:::::: " + detailClass.date);
print("receiptNumber value::::::: " + detailClass.amount);
}
void getDataFromEdit(DetailsClassWhichYouWantToPop detailClass) {
print("natureOfExpense Value:::::: " + detailClass.natureOfExpense);
print("receiptNumber value::::::: " + detailClass.receiptNumber);
}
void pushFilePath(File file) async {
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Screen2(fileObj: file),
),
).then((val){
getDataFromScreen2(val); //you get details from screen2 here
});
}
}
更简单的方法是将正文包裹在 WillPopScope 中,这样它将与顶部的 后退按钮一起工作 并且Android 底部的后退按钮。
这是一个示例,其中两个后退按钮 return false:
final return = Navigator.of(context).push(MaterialPageRoute<bool>(
builder: (BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("New Page"),
),
body: WillPopScope(
onWillPop: () async {
Navigator.pop(context, false);
return false;
},
child: newPageStuff(),
),
);
},
));
在他们建议使用的其他答案中:
leading: BackButton(...)
我发现这适用于顶部的后退按钮,而不适用于 Android 按钮。
无论如何我都包括一个例子:
final return = Navigator.of(context).push(MaterialPageRoute<bool>(
builder: (BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: BackButton(
onPressed: () => Navigator.pop(context, false),
),
title: Text("New Page"),
),
body: newPageStuff(),
);
},
));
试试这个:
void _onBackPressed() {
// Called when the user either presses the back arrow in the AppBar or
// the dedicated back button.
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {
_onBackPressed();
return Future.value(false);
},
child: Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: _onBackPressed,
),
),
),
);
}