web-dev-qa-db-ja.com

Flutter-TabBarView内のListViewはスクロール位置を失います

2つのビューを持つTabBarViewを持つ非常にシンプルなFlutterアプリがあります(Tab 1Tab 2)、そのうちの1つ(Tab 1)には多くの単純なリストビューがありますテキストウィジェット、これに関する問題は、Tab 1のListView要素を下にスクロールした後、Tab 1 toTab 2そして最後にTab 2 toTab 1Tab 1迷子になります。

コードは次のとおりです。

import 'package:flutter/material.Dart';

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: 'Flutter Demo Home Page'),
    );
  }
}

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>
    with SingleTickerProviderStateMixin {
  TabController controller;

  @override
  void initState() {
    super.initState();
    controller = new TabController(
      length: 2,
      vsync: this,
    );
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    var tabs = <Tab>[
      new Tab(icon: new Icon(Icons.home), text: 'Tab 1'),
      new Tab(icon: new Icon(Icons.account_box), text: 'Tab 2')
    ];

    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new TabBarView(controller: controller, children: <Widget>[
        new ListView(children: <Widget>[
          new Column(children: <Widget>[
            new Text('Data 1'),
            new Text('Data 2'),
            new Text('Data 3'),
            new Text('Data 4'),
            new Text('Data 5'),
            new Text('Data 6'),
            new Text('Data 7'),
            new Text('Data 8'),
            new Text('Data 9'),
            new Text('Data 10'),
            new Text('Data 11'),
            new Text('Data 12'),
            new Text('Data 13'),
            new Text('Data 14'),
            new Text('Data 15'),
            new Text('Data 16'),
            new Text('Data 17'),
            new Text('Data 18'),
            new Text('Data 19'),
            new Text('Data 20'),
            new Text('Data 21'),
            new Text('Data 22'),
            new Text('Data 23'),
            new Text('Data 24'),
            new Text('Data 25'),
            new Text('Data 26'),
            new Text('Data 27'),
            new Text('Data 28'),
            new Text('Data 29'),
            new Text('Data 30'),
            new Text('Data 31'),
            new Text('Data 32'),
            new Text('Data 33'),
            new Text('Data 34'),
            new Text('Data 35'),
            new Text('Data 36'),
            new Text('Data 37'),
            new Text('Data 38'),
            new Text('Data 39'),
            new Text('Data 40'),
            new Text('Data 41'),
            new Text('Data 42'),
            new Text('Data 43'),
            new Text('Data 44'),
            new Text('Data 45'),
            new Text('Data 46'),
            new Text('Data 47'),
            new Text('Data 48'),
          ])
        ]),
        new Center(child: new Text('Tab 2'))
      ]),
      bottomNavigationBar: new Material(
        color: Colors.deepOrange,
        child: new TabBar(controller: controller, tabs: tabs),
      ),
    );
  }
}

私は別のクラスでTabBarViewchildrens(Tab 1およびTab 2)を分離しましたが、

@override
  Widget build(BuildContext context) {
  ...
}

各子のメソッド(Tab 1およびTab 2)は、コンテナタブにスワイプするたびに実行されます。

私の質問は:

1.-タブからタブに移動しても、ListViewのスクロールを維持するにはどうすればよいですか?

2.-を実行する方法はありますか

@override
Widget build(BuildContext context) {

}

TabBarView childrens(Tab 1およびTab 2)を別のクラスに分離する場合、メソッドは1回だけですか?つまり、Tab 1とTab 2が作成されたときにデータを取得する必要がある場合、Tabがスワイプされるたびにデータを取得する必要はありません。それは高価です。

3.-一般に、タブにスワイプするたびにタブビュー(変数、データなどを含む)が再構築されるのを防ぐ方法はありますか?

よろしくお願いします。

16
SaloGala

1.-タブからタブに移動してもListViewのスクロールを維持するにはどうすればよいですか?

わかりました。思ったほど簡単ではありませんでしたが、なんとかできたと思います。

私の考えは、HomePageStateでリストビューのオフセットを維持することです。リストビューをスクロールするときは、通知機能からオフセットを取得し、setterで設定します(よりクリーンにして共有してください!)。

次に、リストビューを再構築するときに、メインウィジェットに保存されたオフセットを与えるように要求し、ScrollControllerによってそのオフセットでリストを初期化します。

また、リストビューを変更しました。50個のテキストを持つ1つの列要素があり、それぞれ1つのテキストを持つ50個の要素を使用するためです。気にしないでください:)

コード:

import 'package:flutter/material.Dart';

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: 'Flutter Demo Home Page'),
    );
  }
}

typedef double GetOffsetMethod();
typedef void SetOffsetMethod(double offset);

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>
    with SingleTickerProviderStateMixin {
  TabController controller;
  double listViewOffset=0.0;

  @override
  void initState() {
    super.initState();
    controller = new TabController(
      length: 2,
      vsync: this,
    );
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    var tabs = <Tab>[
      new Tab(icon: new Icon(Icons.home), text: 'Tab 1'),
      new Tab(icon: new Icon(Icons.account_box), text: 'Tab 2')
    ];

    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new TabBarView(
      controller: controller,
      children: <Widget>[
        new StatefulListView(
          getOffsetMethod: () => listViewOffset,
          setOffsetMethod: (offset) => this.listViewOffset = offset,
        ),
        new Center(child: new Text('Tab 2'))
      ]),
      bottomNavigationBar: new Material(
        color: Colors.deepOrange,
        child: new TabBar(controller: controller, tabs: tabs),
      ),
    );
  }
}

class StatefulListView extends StatefulWidget {
  StatefulListView({Key key, this.getOffsetMethod, this.setOffsetMethod}) : super(key: key);

  final GetOffsetMethod getOffsetMethod;
  final SetOffsetMethod setOffsetMethod;

  @override
  _StatefulListViewState createState() => new _StatefulListViewState();
}

class _StatefulListViewState extends State<StatefulListView> {

  ScrollController scrollController;

  @override
  void initState() {
    super.initState();
    scrollController = new ScrollController(
      initialScrollOffset: widget.getOffsetMethod()
    );
  }

  @override
  Widget build(BuildContext context) {
    return new NotificationListener(
      child: new ListView.builder(
        controller: scrollController,
        itemCount: 50,
        itemBuilder: (BuildContext context, int index) {
          return new Text("Data "+index.toString());
        },
      ),
      onNotification: (notification) {
        if (notification is ScrollNotification) {
          widget.setOffsetMethod(notification.metrics.pixels);
        }
      },
    );
  }
}
9
Marcin Szałek

各TabBarViewにPageStorageKeyを指定すると、スクロールオフセットが保存されます。 PageStorageKey here の詳細を参照してください。

26
Jordan Nelson

具体的には、PageStorageKeyをスクロール可能なビューで使用して、スクロール位置を維持できます。例:

new ListView.builder(key: new PageStorageKey('myListView'), ...)
13
Oleg Khalidov

ウィジェットのbuild(BuildContext context)メソッドを一度だけ実行する方法はありますか...

私は、フラッターのアイデアは常に再構築する準備ができていることです。安いはずです。高価なアクションがある場合は、 State を使用して結果を「キャッシュ」できます。例えば。 initStateでネットワーク要求を行い、応答を受信したときにsetState再構築を介してネットワーク要求を行うことができます。タブの場合、親ウィジェットでデータを準備して保存できます。詳細については、 flutter tutorial 状態の管理についてを参照してください。

0
German Saprykin

出力:

enter image description here


コード:

@override
Widget build(BuildContext context) {
  return DefaultTabController(
    length: 2,
    child: Scaffold(
      appBar: AppBar(
        title: Text("PageStorageKey"),
        bottom: TabBar(
          tabs: [
            Tab(icon: Icon(Icons.looks_one), text: "List1"),
            Tab(icon: Icon(Icons.looks_two), text: "List2"),
          ],
        ),
      ),
      body: TabBarView(
        children: [
          _buildList(key: "key1", string: "List1: "),
          _buildList(key: "key2", string: "List2: "),
        ],
      ),
    ),
  );
}

Widget _buildList({String key, String string}) {
  return ListView.builder(
    key: PageStorageKey(key),
    itemBuilder: (_, i) => ListTile(title: Text("${string} ${i}")),
  );
}
0
CopsOnRoad