Flutterページ間のデータ転送(共有)のいくつかの一般的な方法

14374 ワード

前言
Androidでは、ページジャンプ(Frament,Activity)の際に、現在のデータの一部を別のページに持ち込み、別のページで使用するシーンがよくあります.この時、私たちがよく使うのはIntent、Bundleなどの携帯データです.
では、Flutterの開発過程では、ページ間のデータ伝達も欠かせないし、あるページのデータをどのように別のページに伝達(共有)するか、あるいは現在のページを閉じて前のページにデータを与えるか.
この記事では、Flutterでページ間のデータ転送(共有)の一般的な方法とシーンについて説明します.

データ転送を開始する前に、データを転送するクラスを作成します.
Androidでオブジェクトを渡すにはシーケンス化が必要ですSerializableまたはParcelableインタフェースを転送することができ、Flutterではデータ転送にシーケンス化された方法がなく、直接オブジェクトを転送することができます.単純なクラスを定義するには、次のようにします.
///         
class TransferDataEntity {
  String name;
  String id;
  int age;

  TransferDataEntity(this.name, this.id, this.age);
}


データの伝達方法を具体的に見てみましょう
コンストラクタによるデータの転送
コンストラクタによるデータの転送は最も簡単な方法であり、最も一般的な方法でもあります.最初のページでは、データの転送が必要なオブジェクトをシミュレートして作成します.ジャンプをクリックすると、DataTransferByConstructorPageページにデータを渡し、持参したデータをページに表示します.
  • 転送データオブジェクト
    final data = TransferDataEntity("001", "   ", 18);
  • を作成する.
  • DataTransferByConstructorPageページにジャンプする方法
     _transferDataByConstructor(BuildContext context, TransferDataEntity data) {
        Navigator.push(
            context,
            MaterialPageRoute(
                builder: (context) => DataTransferByConstructorPage(data: data)));
      }
  • を定義する
  • DataTransferByConstructorPageページでデータを受信して表示します.コードは次のとおりです.
    私たちは2つのことをする必要があります.
    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: [
              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}"),
              )
            ],
          ),
        );
      }
    }

  • 1ページが閉じると前のページにデータを運ぶ(Navigator.pop)
    Android開発では、前のページにデータを渡すために一般的に使用されている従来の方法はstartActivity ForResult()メソッドです.しかしflutterではそんなに面倒なことはありません.Navigatorを使うだけですpopメソッドはデータ結果を持ち帰ることができます.しかし、私たちがジャンプするときは2つの点に注意する必要があります.

    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方式
    公式サイトの説明:InheritedWidgetはFlatterの中で非常に重要な機能型Widgetであり、Widgetツリーでデータを効率的に下に伝達し、共有することができ、これはWidgetツリーでデータを共有する必要があるシーンで非常に便利であり、例えばFlatterでは、InheritedWidgetを通じてアプリケーショントピック(Theme)とLocle(現在の言語環境)の情報を共有している.
    InheritedWidgetはReactのcontextと同様に機能しており、逐次転送と比較してコンポーネントの階層間転送を実現します.InheritedWidgetのWidgetツリーでのデータ転送方向は上から下であり、これはNotificationの転送方向とは正反対である.
    利点:各Widgetを制御して単独でデータを取り出して使用できる
    1ページに同じレベルのWeigetしか存在しない場合、コンストラクタを使用する方法はもちろん最も簡単で、最も便利ですが、1ページに複数のレベルの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: [
              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 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(BuildContext context) {
        return (context.inheritFromWidgetOfExactType(
                IGenericDataProvider().runtimeType) as IGenericDataProvider).data;
      }
    }

    2.ジャンプを使用する場合の修正コードは以下の通りです(主に汎用サポートの追加)
    _inheritedGenericToPage(BuildContext context, TransferDataEntity data) {
        Navigator.push(
            context,
            MaterialPageRoute(
                builder: (context) => IGenericDataProvider(
                  child: IDataWidget(),
                  data: data,
                )));
      }


    伝達された値を受信する方法は次のとおりです.
    IGenericDataProvider.of(context)は直接値をとることができる
    class IGenericDataWidget extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final data = IGenericDataProvider.of(context);
    
        return Scaffold(
          appBar: AppBar(
            title: Text("Inherited        "),
          ),
          body: Column(
            children: [
              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 {
      @override
      Widget build(BuildContext context) {
        final data = IGenericDataProvider.of(context);
        return Scaffold(
          appBar: AppBar(
            title: Text("        "),
          ),
          body:Column(
            children: [
              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 {
      @override
      Widget build(BuildContext context) {
        //          
        var data = transSingletonData.transData;
        return Scaffold(
          appBar: AppBar(
            title: Text("        "),
          ),
          body: Column(
            children: [
              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}"),
              ),
            ],
          ),
        );
      }
    }


    グローバル・シングル・インスタンスとStreamを組み合わせたデータの転送
  • グローバルを受け入れる単一のオブジェクトを作成し、伝達値をStream方式
  • に変換する.
  • 受信データは、StreamBuilderを用いて直接受信処理することができる
  • .
    class TransferStreamSingleton {
      static final TransferStreamSingleton _instanceTransfer =
          TransferStreamSingleton.__internal();
      StreamController streamController;
    
      void setTransferData(TransferDataEntity transData) {
        streamController = StreamController();
        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 {
    
      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: [
                          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専門的な記事がありますFlutterStream . Flutter Streamの概要と使用の詳細についてStreamおよび一部の操作の使用.
    現在公式に推奨されているproviderは、実際にはInheritedWidgetを使用しています.時間があれば、InheritedWidgetと使用方法を詳しくお勧めします.
    以上はページ間の値の伝達の1つの総括で、本文Demo、もし書く不足な点があるならば、指摘して~