🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

Dart

Dartは、Googleによって開発されたJavaScriptの代替となることを目的に作られた、ウェブ向けのプログラミング言語である。

Q&A

解決済

1回答

3577閲覧

PageViewの中でPageViewを使った際の動作について

Inete6Q

総合スコア18

Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

Dart

Dartは、Googleによって開発されたJavaScriptの代替となることを目的に作られた、ウェブ向けのプログラミング言語である。

0グッド

0クリップ

投稿2021/01/19 23:14

前提・実現したいこと

下記コードがあり、実現したいことが2点あります。
簡潔に説明するとPageViewウィジェットで横にスワイプできるものの中に、PageViewでさらに縦にスワイプできるようにしています。
これを縦にスワイプして別ページに遷移後、横にスワイプしてから反対方向にスワイプし、元のページに戻ると、一番上のページに戻ってしまいます。
横に3枚ページがあり、どれかをスワイプした際に、それぞれのページもリンクしてスワイプしている状態にしたいです。
例でいうと、LeftPageを下にスワイプし、TwoPageに遷移したら、その後左右にスワイプした場合、FivePage,EightPageに遷移するようにしたいです。

もう一点がPageViewの中でFutureBuilderを使用しているのですが、左右にスワイプした際に毎回再描画されないようにしたいです。(説明が正しいのかわからないので補足ですが、スワイプで切り替えた際に毎回非同期処理を行わないようにしたいです。)

発生している問題

・スワイプでページを切り替えた際、毎回再読み込みされてしまう。
・左右スワイプをして切り替えた際に、元のページの一番上のページに戻ってしまう。

該当のソースコード

dart

1import 'package:flutter/material.dart'; 2 3void main() { 4 runApp(new MyApp()); 5} 6 7class MyApp extends StatelessWidget { 8 9 Widget build(BuildContext context) { 10 return new MaterialApp( 11 title: 'Flutter Demo', 12 home: MyHome(), 13 debugShowCheckedModeBanner: false, 14 ); 15 } 16} 17 18class MyHome extends StatelessWidget { 19 20 Widget build(BuildContext context) { 21 return Scaffold( 22 appBar: AppBar( 23 title: Text("title"), 24 ), 25 body: PageView( 26 children: [ 27 LeftPage(), 28 CenterPage(), 29 RightPage(), 30 ], 31 ), 32 ); 33 } 34} 35 36class LeftPage extends StatelessWidget { 37 38 Widget build(BuildContext context) { 39 return FutureBuilder<Object>( 40 future: Future.delayed(const Duration(seconds: 2), () => 100), 41 builder: (context, snapshot) { 42 if (snapshot.hasData) { 43 return PageView( 44 scrollDirection: Axis.vertical, 45 children: [ 46 OnePage(), 47 TwoPage(), 48 ThreePage(), 49 ], 50 ); 51 } 52 53 return Center( 54 child: CircularProgressIndicator(), 55 ); 56 }); 57 } 58} 59 60class CenterPage extends StatelessWidget { 61 62 Widget build(BuildContext context) { 63 return FutureBuilder<Object>( 64 future: Future.delayed(const Duration(seconds: 2), () => 100), 65 builder: (context, snapshot) { 66 if (snapshot.hasData) { 67 return PageView( 68 scrollDirection: Axis.vertical, 69 children: [ 70 FourPage(), 71 FivePage(), 72 SixPage(), 73 ], 74 ); 75 } 76 77 return Center( 78 child: CircularProgressIndicator(), 79 ); 80 }); 81 } 82} 83 84class RightPage extends StatelessWidget { 85 86 Widget build(BuildContext context) { 87 return FutureBuilder<Object>( 88 future: Future.delayed(const Duration(seconds: 2), () => 100), 89 builder: (context, snapshot) { 90 if (snapshot.hasData) { 91 return PageView( 92 scrollDirection: Axis.vertical, 93 children: [ 94 SevenPage(), 95 EightPage(), 96 NinePage(), 97 ], 98 ); 99 } 100 return Center( 101 child: CircularProgressIndicator(), 102 ); 103 }); 104 } 105} 106 107class OnePage extends StatelessWidget { 108 109 Widget build(BuildContext context) { 110 return Center( 111 child: Text("one page"), 112 ); 113 } 114} 115 116class TwoPage extends StatelessWidget { 117 118 Widget build(BuildContext context) { 119 return Center( 120 child: Text("two page"), 121 ); 122 } 123} 124 125class ThreePage extends StatelessWidget { 126 127 Widget build(BuildContext context) { 128 return Center( 129 child: Text("three page"), 130 ); 131 } 132} 133 134class FourPage extends StatelessWidget { 135 136 Widget build(BuildContext context) { 137 return Center( 138 child: Text("four page"), 139 ); 140 } 141} 142 143class FivePage extends StatelessWidget { 144 145 Widget build(BuildContext context) { 146 return Center( 147 child: Text("five page"), 148 ); 149 } 150} 151 152class SixPage extends StatelessWidget { 153 154 Widget build(BuildContext context) { 155 return Center( 156 child: Text("six page"), 157 ); 158 } 159} 160 161class SevenPage extends StatelessWidget { 162 163 Widget build(BuildContext context) { 164 return Center( 165 child: Text("seven page"), 166 ); 167 } 168} 169 170class EightPage extends StatelessWidget { 171 172 Widget build(BuildContext context) { 173 return Center( 174 child: Text("eight page"), 175 ); 176 } 177} 178 179class NinePage extends StatelessWidget { 180 181 Widget build(BuildContext context) { 182 return Center( 183 child: Text("nine page"), 184 ); 185 } 186}

試したこと

下記方法と近いかなと思っていろいろ試したのですがうまくいきませんでした。
[https://qiita.com/umechanhika/items/a2aca1ead61045803a02]

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

2つの問題が発生しているようです。

まず1つ目
PageViewはスクロール位置を記憶しないのでbuildが再度呼び出されるタイミングでスクロール位置が初期化してしまいます。再buildはFlutterのメモリや描画の最適化で発生するものなので防げませんが、再buildが発生してもスクロール位置を記憶しておくことによりスクロール位置を自然に復元することができます。

下記のようにPageViewのkeyプロパティにPageStorageKeyを指定します。

dart

1PageView( 2 key: PageStorageKey('page1'), // Stringである必要はありません 3 .. 4);

二つ目の問題はFutureBuilderが毎回実行されているのでなんらかの方法で状態(State)を記憶しておく事をおすすめします。Flutterでアプリを開発する場合ほぼすべての場合になんらかの状態管理(State Management)が必要になってきます。なかなか広いテーマなので詳しくは検索してください。
https://flutter.dev/docs/development/data-and-backend/state-mgmt
StatefulWidgetやProviderが有名ですが、Providerと同じ作者が作っているより新しいRiverPodがおすすめです。
https://pub.dev/packages/flutter_riverpod

サンプルをRiverPodを使って整理してみました。先にpubspecにriverpodを追加してflutter pub getしてください。

  • 縦スクロールするWidget単位でロードする例です。ページごとにロードする場合は設計が少し変わります。
  • コードはもう少し最適化できますが、RiverPodを知らないとややこしくなるのでここまでにしておきます。
  • 最終的に何を作りたいのがわかればより良い設計を提案できます。

dart

1import 'package:flutter/material.dart'; 2import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 4final verticalScrollingPageCtrl = FutureProvider.family<Object, String>((ref, id) async { 5 // 6 // Do something... 7 // 8 await Future.delayed(const Duration(seconds: 2), () => 100); 9 10 return /* return some value */; 11}); 12 13void main() { 14 runApp(ProviderScope(child: MyApp())); 15} 16 17class MyApp extends StatelessWidget { 18 19 Widget build(BuildContext context) { 20 return new MaterialApp( 21 title: 'Flutter Demo', 22 home: MyHome(), 23 debugShowCheckedModeBanner: false, 24 ); 25 } 26} 27 28class MyHome extends StatelessWidget { 29 30 Widget build(BuildContext context) { 31 return Scaffold( 32 appBar: AppBar( 33 title: Text("title"), 34 ), 35 body: PageView( 36 children: [ 37 VerticalScrollingPage( 38 id: 'scroller1', 39 pages: [ 40 SimplePage(title: 'page1'), 41 SimplePage(title: 'page2'), 42 SimplePage(title: 'page3'), 43 ], 44 ), 45 VerticalScrollingPage( 46 id: 'scroller2', 47 pages: [ 48 SimplePage(title: 'page4'), 49 SimplePage(title: 'page5'), 50 SimplePage(title: 'page6'), 51 ], 52 ), 53 VerticalScrollingPage( 54 id: 'scroller3', 55 pages: [ 56 SimplePage(title: 'page7'), 57 SimplePage(title: 'page8'), 58 SimplePage(title: 'page9'), 59 ], 60 ), 61 ], 62 ), 63 ); 64 } 65} 66 67class VerticalScrollingPage extends StatelessWidget { 68 final String id; 69 final List<Widget> pages; 70 VerticalScrollingPage({ this.id, this.pages}); 71 72 73 Widget build(BuildContext context) => Consumer(builder: (ctx, watch, _) { 74 final asyncValue = watch(verticalScrollingPageCtrl(id)); 75 return asyncValue.maybeWhen( 76 data: (_) => PageView( 77 key: PageStorageKey(id), 78 scrollDirection: Axis.vertical, 79 children: pages, 80 ), 81 orElse: () => Center( 82 child: CircularProgressIndicator(), 83 ), 84 ); 85 }); 86} 87 88class SimplePage extends StatelessWidget { 89 final String title; 90 SimplePage({ this.title}); 91 92 93 Widget build(BuildContext context) { 94 return Center( 95 child: Text(title), 96 ); 97 } 98} 99

投稿2021/01/22 09:59

hiroshihorie

総合スコア192

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Inete6Q

2021/01/24 01:14

ありがとうございます。 もう少し詳しくお聞きしたいのでMENTAのほうでお伺いしようかと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問