在Android中,咱們常遇到的場景就是在頁面跳轉(Frament,Activity)時候,要將當前的部分數據攜帶到另一個頁面中,供另外頁面使用。這時候咱們經常使用的就是使用Intent, Bundle等攜帶數據。bash
那麼在Flutter的開發過程當中,頁面之間的數據傳遞也是必不可少的,又是怎麼把一個頁面的數據傳遞(共享)給另一個頁面,或者關閉當前頁面並把當前頁面的數據帶給前一個頁面。markdown
本篇文章將會介紹Flutter中,頁面面之間的數據傳遞(共享)的幾種常見方式及場景。app
在開始數據傳遞以前咱們先建立一個傳遞數據的類less
在Android中傳遞對象咱們須要序列化實現
Serializable
或者Parcelable
接口才能被傳遞,在Flutter中數據傳遞沒有序列化的方法,直接就能夠傳遞對象。定義一個簡單的類以下:異步
///用來傳遞數據的實體
class TransferDataEntity {
String name;
String id;
int age;
TransferDataEntity(this.name, this.id, this.age);
}
複製代碼
咱們具體看看數據傳遞的方式async
經過構造器傳遞數據是一種最簡單的方式,也是最經常使用的方式,在第一個頁面,咱們模擬建立一個咱們須要傳遞數據的對象。當點擊跳轉的時候,咱們把數據傳遞給DataTransferByConstructorPage頁面,並把攜帶過來的數據展現到頁面上。ide
建立一個傳遞數據對象post
final data = TransferDataEntity("001", "張三丰", 18);
複製代碼
定義一個跳轉到DataTransferByConstructorPage頁面的方法ui
_transferDataByConstructor(BuildContext context, TransferDataEntity data) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DataTransferByConstructorPage(data: data)));
}
複製代碼
在DataTransferByConstructorPage頁面接收到數據並展現出來,代碼以下this
咱們只須要作兩件事:
1.提供一個final變量
final TransferDataEntity data
2.提供一個構造器接收參數
DataTransferByConstructorPage({this.data});
///經過構造器的方式傳遞參數
class DataTransferByConstructorPage extends StatelessWidget {
final TransferDataEntity data;
DataTransferByConstructorPage({this.data});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("構造器方式"),
),
body: Column(
children: <Widget>[
Container(
width: double.infinity,
height: 40.0,
alignment: Alignment.center,
child: Text(data.id),
),
Container(
width: double.infinity,
height: 40.0,
alignment: Alignment.center,
child: Text(data.name),
),
Container(
width: double.infinity,
height: 40.0,
alignment: Alignment.center,
child: Text("${data.age}"),
)
],
),
);
}
}
複製代碼
在Android開發中咱們須要將數據傳遞給上一個頁面一般使用的傳統方式是startActivityForResult()方法。可是在flutter就不用這麼麻煩了。只須要使用Navigator.pop方法便可將數據結果帶回去。可是咱們跳轉的時候須要注意兩點:
1.咱們須要定義一個異步方法用於接收返回來的結果
///跳轉的時候咱們須要使用異步等待回調結果 dataFromOtherPage 就是返回的結果
_toTransferForResult(BuildContext context, TransferDataEntity data) async {
final dataFromOtherPage = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => TransferRouterPage(data: data)),
) as TransferDataEntity;
}
複製代碼
2.在咱們要關閉的頁面使用Navigator.pop 返回第一個頁面
//返回並攜帶數據
_backToData(BuildContext context){
var transferData = TransferDataEntity("嘻嘻哈哈","007",20);
Navigator.pop(context,transferData);
}
複製代碼
官網給出的解釋:InheritedWidget是Flutter中很是重要的一個功能型Widget,它能夠高效的將數據在Widget樹中向下傳遞、共享,這在一些須要在Widget樹中共享數據的場景中很是方便,如Flutter中,正是經過InheritedWidget來共享應用主題(Theme)和Locale(當前語言環境)信息的。
InheritedWidget和React中的context功能相似,和逐級傳遞數據相比,它們能實現組件跨級傳遞數據。InheritedWidget的在Widget樹中數據傳遞方向是從上到下的,這和Notification的傳遞方向正好相反。
優勢:能夠控制每一個Widget單獨去取數據並使用
若是一個頁面只存在同一層級的Weiget這時候使用構造器的方式固然是最簡單的,也是最方便的,可是若是一個頁面存在多個層級的Weiget這時候構造器的方法就有了侷限性,這時候咱們使用InheritedWidget 是一個比較好的選擇。
使用InheritedWidget
方式以下幾步:
繼承InheritedWidget提供一個數據源
class IDataProvider extends InheritedWidget{
final TransferDataEntity data;
IDataProvider({Widget child,this.data}):super(child:child);
@override
bool updateShouldNotify(IDataProvider oldWidget) {
return data!=oldWidget.data;
}
static IDataProvider of(BuildContext context){
return context.inheritFromWidgetOfExactType(IDataProvider);
}
}
複製代碼
定義頁面跳轉時候攜帶數據的方法
///跳轉到IDataWidget頁面並攜帶數據
_inheritedToPage(BuildContext context, TransferDataEntity data) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => IDataProvider(
child: IDataWidget(),
data: data,
)));
}
複製代碼
跳轉的到的頁面並展現數據代碼以下
class IDataWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final data = IDataProvider.of(context).data;
return Scaffold(
appBar: AppBar(
title: Text("Inherited方式傳遞數據"),
),
body: Column(
children: <Widget>[
Container(
alignment: Alignment.center,
height: 40.0,
child: Text(data.name),
),
Container(
alignment: Alignment.center,
height: 40.0,
child: Text(data.id),
),
Container(
alignment: Alignment.center,
height: 40.0,
child: Text("${data.age}"),
),
IDataChildWidget()
],
),
);
}
}
class IDataChildWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final data = IDataProvider.of(context).data;
return Container(
child: Text(data.name),
);
}
}
複製代碼
咱們將上面的
IDataProvier
進行改造加入泛型就能夠通用了
1.修改後的Provider類以下
class IGenericDataProvider<T> extends InheritedWidget {
final T data;
IGenericDataProvider({Key key, Widget child, this.data})
: super(key: key, child: child);
@override
bool updateShouldNotify(IGenericDataProvider oldWidget) {
return data != oldWidget.data;
}
static T of<T>(BuildContext context) {
return (context.inheritFromWidgetOfExactType(
IGenericDataProvider<T>().runtimeType) as IGenericDataProvider<T>).data;
}
}
複製代碼
2.使用跳轉的時候修改代碼以下(主要是添加泛型支持)
_inheritedGenericToPage(BuildContext context, TransferDataEntity data) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => IGenericDataProvider<TransferDataEntity>(
child: IDataWidget(),
data: data,
)));
}
複製代碼
接收傳遞的值的方式以下
IGenericDataProvider.of(context) 能夠直接取值
class IGenericDataWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final data = IGenericDataProvider.of<TransferDataEntity>(context);
return Scaffold(
appBar: AppBar(
title: Text("Inherited泛型方式傳遞數據"),
),
body: Column(
children: <Widget>[
Container(
alignment: Alignment.center,
height: 40.0,
child: Text(data.name),
),
Container(
alignment: Alignment.center,
height: 40.0,
child: Text(data.id),
),
Container(
alignment: Alignment.center,
height: 40.0,
child: Text("${data.age}"),
),
],
),
);
}
}
複製代碼
這種方式咱們仍是使用InheritedWidget,區別就是咱們不是跳轉的時候去建立IGenericDataProvider。而是把他放在最頂層
注意:這種方式必定要把數據放在頂層
定義頂部數據
class MyApp extends StatelessWidget {
// This widget is the root of your application.
//傳遞值的數據
var params = InheritedParams();
@override
Widget build(BuildContext context) {
return IGenericDataProvider(
data: params,
child: MaterialApp(
title: 'Data Transfer Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Data Transfer Demo'),
),
);
}
}
複製代碼
接收數據的方式基本和
InheritedWidget
相同final data = IGenericDataProvider.of(context),獲取數據
class InheritedParamsPage extends StatefulWidget {
@override
_InheritedParamsPageState createState() => _InheritedParamsPageState();
}
class _InheritedParamsPageState extends State<InheritedParamsPage> {
@override
Widget build(BuildContext context) {
final data = IGenericDataProvider.of<TransferDataEntity>(context);
return Scaffold(
appBar: AppBar(
title: Text("經過全局數據方式"),
),
body:Column(
children: <Widget>[
Container(
alignment: Alignment.center,
height: 40.0,
child: Text(data.name),
),
Container(
alignment: Alignment.center,
height: 40.0,
child: Text(data.id),
),
Container(
alignment: Alignment.center,
height: 40.0,
child: Text("${data.age}"),
),
],
),
);
}
}
複製代碼
這種方式就是建立一個全局單例對象,任何地方均可以操控這個對象,存儲和取值均可以經過這個對象
class TransferDataSingleton {
static final TransferDataSingleton _instanceTransfer =
TransferDataSingleton.__internal();
TransferDataEntity transData;
factory TransferDataSingleton() {
return _instanceTransfer;
}
TransferDataSingleton.__internal();
}
final transSingletonData = TransferDataSingleton();
複製代碼
_singletonDataTransfer(BuildContext context) {
var transferData = TransferDataEntity("二汪", "002", 25);
transSingletonData.transData = transferData;
Navigator.push(context,
MaterialPageRoute(builder: (context) => TransferSingletonPage()));
}
複製代碼
class TransferSingletonPage extends StatefulWidget {
@override
_TransferSingletonPageState createState() => _TransferSingletonPageState();
}
class _TransferSingletonPageState extends State<TransferSingletonPage> {
@override
Widget build(BuildContext context) {
//直接引入單例對象使用
var data = transSingletonData.transData;
return Scaffold(
appBar: AppBar(
title: Text("全局單例傳遞數據"),
),
body: Column(
children: <Widget>[
Container(
alignment: Alignment.center,
height: 40.0,
child: Text(data.name),
),
Container(
alignment: Alignment.center,
height: 40.0,
child: Text(data.id),
),
Container(
alignment: Alignment.center,
height: 40.0,
child: Text("${data.age}"),
),
],
),
);
}
}
複製代碼
class TransferStreamSingleton {
static final TransferStreamSingleton _instanceTransfer =
TransferStreamSingleton.__internal();
StreamController streamController;
void setTransferData(TransferDataEntity transData) {
streamController = StreamController<TransferDataEntity>();
streamController.sink.add(transData);
}
factory TransferStreamSingleton() {
return _instanceTransfer;
}
TransferStreamSingleton.__internal();
}
final streamSingletonData = TransferStreamSingleton();
複製代碼
_streamDataTransfer(BuildContext context) {
var transferData = TransferDataEntity("三喵", "005", 20);
streamSingletonData.setTransferData(transferData);
Navigator.push(context,
MaterialPageRoute(builder: (context) => TransferStreamPage()));
}
複製代碼
class TransferStreamPage extends StatefulWidget {
@override
_TransferStreamPageState createState() => _TransferStreamPageState();
}
class _TransferStreamPageState extends State<TransferStreamPage> {
StreamController _streamController = streamSingletonData.streamController;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("全局單例結合Stream"),
),
body: StreamBuilder(
stream: _streamController.stream,
initialData: TransferDataEntity("", "", 0),
builder: (context, snapshot) {
return Column(
children: <Widget>[
Container(
alignment: Alignment.center,
height: 40.0,
child: Text(snapshot.data.name),
),
Container(
alignment: Alignment.center,
height: 40.0,
child: Text(snapshot.data.id),
),
Container(
alignment: Alignment.center,
height: 40.0,
child: Text("${snapshot.data.age}"),
),
],
);
}));
}
@override
void dispose() {
_streamController.close();
super.dispose();
}
}
複製代碼
以上是咱們在在Flutter中經常使用的幾種頁面之間傳遞數據的方式,其中最後一種方式提到了
Stream
和StreamBuilder
我有一篇文章專門介紹了Flutter
的Stream
。Flutter Stream簡介及使用 詳細的介紹了Stream
及部分操做的使用。如今官方推薦的provider實際上就是使用了
InheritedWidget
有時間的話建議詳細了下InheritedWidget
及使用方法。以上是對頁面之間值傳遞的一個總結,本文Demo,若有寫的不足之處,望指正~