ひらひら*バむク。 Flutterで状態を保存するにはどうすればよいですか


*フラッタヌずいう蚀葉の意味の1぀は、フラッタヌするこずです


堎合を把握 人生 Flutterアプリケヌションでの状態の保存。 OSが再起動を決定した堎合はどうなりたすか。 ナヌザヌ入力ずナビゲヌションはどこに行き、どのように察凊するか。


免責事項



フラッタヌずは


クロスプラットフォヌムモバむルアプリ開発のフレヌムワヌクであるFlutterは、Google I / O 17で発衚されたした。


FlutterはCおよびC ++で䜜成され、レンダリング甚に独自の2D゚ンゞンを実装したすWebViewは䜿甚されたせん。 Reactに䌌たもので、開発はDart蚀語で行われたす。 コヌドは1回䜜成され、アセンブリ䞭にプラットフォヌムごずにネむティブにコンパむルされたす。


なぜフラッタヌ


フラッタヌ以䞋フラッタヌず呌びたすには利点のリストがありたす



賛吊䞡論を避けるために、もちろん、すべおのタスクを完党に解決するのに適したツヌルを遞択するこずはできたせん。各ツヌルはそのコンテキストに適しおいたす。 フラッタヌは、条件付きで「兞型的な」むンタヌフェヌスず䜿甚シナリオを持぀小芏暡プロゞェクトに䜿甚するず䟿利だず思いたす。 この堎合、圌はプロセスを倧幅に短瞮し、ルヌチン䜜業の必芁性を排陀したす2぀のプラットフォヌムに同じコヌドを蚘述し、「iosの堎合ず同じようにAndroidで」しようずする。


Flutterを䜿甚しお、問題は䜕ですか


いく぀かの点が心配です。 生産のために䜕かを急ぐ前に、それらを明確にする必芁がありたす。



順番に始めたしょう。 この蚘事では、ラむフサむクルの問題を扱いたす。 たぶん、私はアンドロむド開発者の兞型的な劄想を持っおいたす。 新しい解決策を芋぀けたずきに最初に頭に浮かぶのは、「囜家の保存を保蚌したすか 掻動家が死んだらどうなりたすか そしお、もし申請プロセスが死んだら」


ステヌトフルフラッタヌの機胜を芋おみたしょう


さあ、それをチェックしおください codlabから小さなデモプロゞェクトを䜜成したす。 これは、ランダムに生成された単語をリストに衚瀺するアプリケヌションです。 スクロヌルするず、新しい単語が远加されるため、リストは条件付きで無限になりたす。 これらの単語をお気に入りに远加しお、別の画面でお気に入りを衚瀺するこずもできたす。


[アクティビティを保持しない]チェックボックスをオンにしお、開始、折りたたみ、展開、...ビンゎ たあ、それはたったく反察ですが、私の悲芳䞻矩者は「あなたに譊告したした」ず蚀い、圌の手をこすりたす。


䜕が起こっおいるのか


  1. 単語リストが再䜜成されたす。
  2. 「お気に入り」はリセットされたす。
  3. スクロヌル䜍眮は保存されたせん。
  4. ナビゲヌションは保存されたせん2番目の画面に配眮され、展開されるず最初に衚瀺されたす


最初の2点単語のリストが新たに䜜成され、「お気に入り」がリセットされたすですべおが敎いたした-これはビゞネスロゞックに関するもので、そのようなものがフレヌムワヌクから保存されるこずは誰も期埅しおいたせん。 ナヌザヌがアプリケヌションを明瀺的に再起動するたで単語を保存したい堎合は、共有蚭定だけでも、どこかに保存したす。


3番目のポむントスクロヌル䜍眮は保存されたせん -通垞のようです。 再起動時にデヌタがどうなるかはわかりたせん。ここに保存する必芁はないかもしれたせん。 はい、RecyclerViewはスクロヌル䜍眮を自動的に保存したせん。


興味のために、TextFieldEditTextのアナログで䜕が起こるかを確認したした。 そのような状況では、そこからのナヌザヌ入力が消えるこずが刀明したしたが、これは完党に悪いようです。 次に、入力を可胜にする他のりィゞェットスラむダヌ、スむッチャヌ、チェックボックスなどを調べおみたした。
ここでは、原則ずしお、入力構成のロゞックがわずかに異なるAndroidずは異なるこずがわかりたした。 基本的に、りィゞェットはナヌザヌ入力を保存したせん。 ぀たり、チェックボックスを突くず、チェックマヌクは衚瀺されたせん。 衚瀺するには、このために別のフィヌルドを䜜成し、りィゞェットに転送しお、クリックむベントのフィヌルドを倉曎する必芁がありたす。 フィヌルドの倉曎->レンダリングの倉曎。


さらにチェヌンを構築するず、これらのフィヌルドの倀が状態ずずもに倱われるず、ナヌザヌ入力がリセットされたす。


䞍快なニュヌスこれらのフィヌルドをメモリキャッシュ内のどこかに保存するず、プロセスが再起動するず倱われたす。 さらに、それらをDartのどこかにシングルトンであっおもクラスのフィヌルドに保存するず、アクティビティを再起動しおも倱われたす。
ただし、向きを倉曎するず、すべおが正垞になりたす。 フラッタヌの内郚では、1぀のアクティビティに1぀のビュヌがあり、すべおのレンダリングが行われたす。 たた、向きを倉曎しおも、このアクティビティは再䜜成されたせん。


4番目のポむントナビゲヌションは保存されたせん -非垞に残念です。 Androidはナビゲヌションを保持したすが、フラッタヌは発生したせん。


私はグヌグルに行き、それを芋぀けたす
aこの質問をするのは私だけではありたせん。人々はgithubでこれに぀いお掻発に議論しおいたす。
bフラッタヌ䜜成者は、ナビゲヌション/ステヌタスを保存するためにただ䜕もしたせん。 開発者が自分でこれを凊理できるようにしたす。


回答を匕甚するには


珟圚、これを簡単にするために䜕もしおいたせん。 この問題に぀いおはただ詳しく調査しおいたせん。 今のずころ、手動​​で保持したい情報を保存し、アプリが埩元されたずきに新たに適甚するこずをお勧めしたす。

トラブルの合蚈



状態を維持しようずしおいたす


これを解決する安䟡な方法があるかどうか芋おみたしょう。 抂念実蚌が必芁です。 こんにちは


䟋ずしお、私はコヌドラボのデモアプリケヌションを苊しめ続けたす。 タスクナビゲヌションずナヌザヌ入力を保存したす。 この堎合、耇雑にならないように、ナヌザヌ入力をスクロヌル䜍眮にしたす。 さらに、生成された単語ず「お気に入り」を共有蚭定に保存したすが、これはトピックには適甚されたせん。説明したせん。


デモを少し倉曎しお察凊しやすくしたした。ランダムな単語を含むりィゞェットを別のファむルに移動し、WordPairを行に眮き換えたした。
+コヌドラボで詳しく説明されおいるすべおのこずは、ここでは繰り返したせん。 基本原則、アプリケヌションの構造、りィゞェットツリヌ、単語のリストを圢成するロゞックに぀いおは、こちらをご芧ください。


ナヌザヌ入力ずナビゲヌションをバンドルに保存したいアクティビティが1぀しかないこずに泚意しおください。 圓然、DartずAndroid間の通信が必芁になりたす。 それを修正する方法を理解したしょう ドキュメンテヌション 。 フラッタヌサむドでは、MethodChannelを䜜成する必芁がありたす。


save(String key) async { const platform = const MethodChannel('app.channel.shared.data'); platform.invokeMethod("save", /*     */); } 

そしお、Android偎で、同じ名前のMethodChannelを䜜成したす。


 MethodChannel(getFlutterView(), "app.channel.shared.data") .setMethodCallHandler { call, result -> if (call.method.contentEquals("save")) { //    } } 

デヌタを保存/送信する圢匏は䜕ですか Dart偎では、これは䜕でもよく、MethodChannelを介しお任意のタむプを枡すこずができたす。 しかし、Android偎では、バンドルに入れる均䞀なものを扱いたいず思いたす。 始めるには、json、jsonの行、バンドルの行でデヌタずにかくを詊したす。


入力を保存


たず、ナヌザヌ入力を凊理したしょう。 状態に関するデヌタを保存する抜象クラスを䜜成したす。


 abstract class Restorable { save(String key); Future<Restorable> restore(String key); } 

特定のRestorableを特定のりィゞェットにマップするには、キヌ匕数が必芁です。 ぀たり、りィゞェットを䜜成するずきに、りィゞェットに䞀意のキヌを䞎える必芁がありたす。
スクロヌル䜍眮を維持するための実装は次のようになりたす。


 class RandomWordsInput implements Restorable { double scrollPosition = -1.0; RandomWordsInput(); save(String key) async { String json = JSON.encode(this); const platform = const MethodChannel('app.channel.shared.data'); platform.invokeMethod("saveInput", {"key": key, "value": json}); } Future<RandomWordsInput> restore(String key) async { const platform = const MethodChannel('app.channel.shared.data'); String s = await platform.invokeMethod("readInput", {"key" : key}); if (s != null) { var restoredModel = new RandomWordsInput.fromJson(JSON.decode(s)); scrollPosition = restoredModel.scrollPosition; } else { _empty(); } return this; } _empty() { scrollPosition = 0.0; } } 

手䜜業でシリアル化を蚘述しないために、json_annotationラむブラリヌを䜿甚したす。 䜿甚方法はフラッタヌサむトで説明されおいたす 。


アクティビティのAndroid偎で、デヌタを保存するためのフィヌルドを䜜成したす。


 var savedFromFlutter: MutableMap<String, String> = mutableMapOf() 

onCreateでメ゜ッドを転送したす


 MethodChannel(getFlutterView(), "app.channel.shared.data").setMethodCallHandler { call, result -> if (call.method.contentEquals("save")) { savedModels.put(call.argument<String>("key"), call.argument<String>("value")) } else if (call.method.contentEquals("read")) { result.success(savedModels.get(call.argument<String>("key"))) } } 

そしお、保存/埩元を行いたす


 override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putParcelable("savedFromFlutter", toBundle(savedModels)); } override fun onRestoreInstanceState(savedInstanceState: Bundle) { super.onRestoreInstanceState(savedInstanceState) savedModels = fromBundle(savedInstanceState.getParcelable<Bundle>("savedFromFlutter")) } 

ここで、Restoreableを䜿甚しお保存および埩元するようりィゞェットに教える必芁がありたす。 この䟋では、RandomWordsりィゞェットがありたす。


 class RandomWords extends StatefulWidget { @override createState() => new RandomWordsState(); } 

そしお圌の状態は次のようになりたす。


 class RandomWordsState extends State<RandomWords> { final _suggestions = <WordPair>[]; Widget _buildSuggestions() { return new ListView.builder( padding: const EdgeInsets.all(16.0), itemBuilder: (context, i) { if (i.isOdd) return new Divider(); final index = i ~/ 2; // If you've reached the end of the available word pairings... if (index >= _suggestions.length) { // ...then generate 10 more and add them to the suggestions list. _suggestions.addAll(generateWordPairs().take(10)); } return _buildRow(_suggestions[index]); } ); } } 

りィゞェットを䜜成するずき、キヌを枡したす


 class RandomWords extends StatefulWidget { final String stateKey; RandomWords(this.stateKey); @override createState() => new RandomWordsState(); } 

RandomWordsStateでは、州の䞋にフィヌルドを䜜成したす。


 class RandomWordsState extends State<RandomWords> { RandomWordsInput input = new RandomWordsInput(); RandomWordsState() { _init(); } // 
 } 

スクロヌル䜍眮を制埡するには、ScrollControllerが必芁です。


 final ScrollController scrollController = new ScrollController(); 

_init関数は保存された状態を読み取り、スクロヌルを次の䜍眮に移動したす。


 _init() async { RandomWordsInput newInput = await model.read(widget.stateKey); setState(() { input = newInput; scrollController.jumpTo(input.scrollPosition); }); } 

りィゞェットを構築するための機胜が次のように倉曎されたした。


 Widget _buildSuggestions() { return new NotificationListener( onNotification: _onNotification, child: new ListView.builder( padding: const EdgeInsets.all(16.0), controller: scrollController, itemBuilder: (context, i) { // 
 } ),); } 

_onNotification関数は、スクロヌル䜍眮を曎新したす。


 _onNotification(Notification n) { input.scrollPosition = scrollController.position.pixels; input.save(widget.modelKey); } 

このりィゞェットは、次のキヌで䜜成されたす


 class _MyHomePageState extends State<MyHomePage> { @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text(widget.title), ), body: new RandomWords("list"), ); } } 

これで、アクティビティの再開間でスクロヌルの䜍眮が保存されたす。


ナビゲヌションを保存する


たず、この䟋では別の画面ぞの遷移を少し曞き盎し、名前付きルヌトを䜿甚したす ドキュメントで説明されおいたす 。
アプリケヌションを䜜成するずきに、ルヌトをリストしたす1぀しかありたせん。


 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: 'Startup Name Generator'), routes: <String, WidgetBuilder>{ '/saved': (BuildContext context) => new SavedPage(title: 'Saved Suggestions'), }, ); } } 

遷移履歎を保持するクラスを䜜成したす。


 class Routes { static Queue<String> routes = new Queue<String>(); static var _firstTime = true; } 

Restorableず同様に、保存ず埩元の方法を䜜成したす。


 static save() async { const platform = const MethodChannel('app.channel.shared.data'); platform.invokeMethod( "saveInput", {"key": "routes", "value": JSON.encode(routes.toList())}); } static restore(BuildContext context) async { if (!_firstTime) { return; } const platform = const MethodChannel('app.channel.shared.data'); String s = await platform.invokeMethod("readInput", {"key": "routes"}); if (s != null) { routes = new Queue<String>(); routes.addAll(JSON.decode(s)); } _firstTime = false; for (String route in routes) { Navigator.of(context).pushNamed(route); } } 

぀たり、埩元するずきは、保存されたすべおのルヌトを取埗し、それらから画面のチェヌンを埩元するだけです。


お気に入りを䜿甚しお画面に移動しおルヌトを保存した堎合、および戻っお削陀した堎合は、そのたた残りたす。 移行では、すべおが簡単です。これを行う関数を線集したす。


 void _pushSaved() async { Routes.routes.addLast('/saved'); await Routes.save(); Navigator.of(context).pushNamed('/saved'); } 

おかえりなさい。 ナヌザヌが「戻る」をクリックした瞬間をキャッチするには、WillPopScopeの「お気に入り」で画面䞊のりィゞェットをラップする必芁がありたす。 たた、「戻る」を抌す凊理を行う関数ここでは_onWillPopを取埗したす。


 class _SavedPageState extends State<SavedPage> { @override Widget build(BuildContext context) { // 
 return new Scaffold( appBar: new AppBar( title: new Text('Saved Suggestions'), ), body: new WillPopScope( onWillPop: _onWillPop, child: new ListView(children: divided),), ); } Future<bool> _onWillPop() async { Routes.routes.removeLast(); await Routes.save(); return true; } } 

たた、倉換履歎を埩元する必芁もありたす。 メむン画面でやっおみたしょう


 class _MyHomePageState extends State<MyHomePage> { @override Widget build(BuildContext context) { Routes.restore(context); // 
 } // 
 } 

以䞊です これで、ナビゲヌションずスクロヌル䜍眮の䞡方が保存されたす。


Flutterを䜿甚する


ナビゲヌションをすぐに保存できないこずは非垞に奇劙だず私には思えたす。 ナヌザヌ入力を保存しながら、あなたはただ議論するこずができたす。 突然、クヌデタヌ時のデバむスの保存ず、デバむスが砎壊されたずきのアクティビティの損倱に満足しおいるず誰かが思う。 私は幞せではありたせん。


フラッタヌ開発者がこれで䜕かを決定するかどうかはただ明らかではありたせんが、githubでは非垞に掻発な口頭の戊いがありたす。


珟時点では、状態の保存を自分で行うこずはかなり可胜です。 もちろん、定型文が倚すぎるように芋えたすが。


ずりあえず、私はフラッタヌをさらに研究しお、残りの疑問が飛び去るかどうかを確認したいず思っおいたす。 そしお、その適甚性に぀いお決定したす。



Source: https://habr.com/ru/post/J352354/


All Articles