Flutter-ListView внутри TabBarView теряет позицию прокрутки

у меня есть очень простое приложение Flutter с TabBarView С двумя представлениями (1 и Tab 2), один из них (1) имеет ListView со многими простыми текстовыми виджетами, проблема в том, что после прокрутки вниз элементов ListView 1, если я красть от 1 to Tab 2 и, наконец, я салфетки из Tab 2 to 1, Предыдущее положение прокрутки в ListView в размере 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),
      ),
    );
  }
}

Я даже разделил TabBarView для детей (вкладка 1 и вкладка 2) в других классах, и я заметил, что

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

метод каждого ребенка (вкладка 1 и вкладка 2) выполняется каждый раз, когда я провожу по вкладке контейнера.

мои вопросы:

1.- Как я могу сохранить свиток ListView даже если я перехожу с вкладки на вкладку?

2.- Есть ли способ выполнить

@override
Widget build(BuildContext context) {

}

способ только один раз, если я отделяю TabBarView childrens (вкладка 1 и вкладка 2) в другие классы? Я имею в виду, что если мне нужно получить данные при создании вкладки 1 и вкладки 2, я не хочу делать это каждый раз, когда вкладка прокручивается. Это будет дорого стоить.

3.- В общем, есть способ предотвратить это представление вкладки (включая переменные, данные и т. д.) перестраиваться каждый раз, когда я провожу по этой вкладке?

спасибо заранее.

3 ответов


1.- Как я могу сохранить прокрутку ListView, даже если я перемещаюсь с вкладки на вкладку?

хорошо, это было не так просто, как я думал, но я думаю, что мне удалось это сделать.

моя идея-сохранить смещение listview в HomePageState, и когда мы прокручиваем listview, мы просто получаем смещение от notifier и устанавливаем его через setter (пожалуйста, сделайте его чище и поделитесь!).

затем, когда мы перестраиваем listview, мы просто просим наш основной виджет дать нам сохраненное смещение и ScrollController мы инициализируем список с этим смещением.

Я также изменил ваш listview, так как он имел один элемент столбца с 50 текстами, чтобы использовать 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);
        }
      },
    );
  }
}

Если вы дадите каждому TabBarView PageStorageKey смещение прокрутки будет сохранено. Смотрите дополнительную информацию о компании PageStorageKey здесь.


есть ли способ выполнить метод сборки виджета (BuildContext context) только один раз...

имхо, идея флаттера-всегда быть готовым к перестройке. Это должно быть дешево. Если у вас есть какие-то дорогостоящие действия, вы можете использовать государство в "кэш" результаты. Е. Г. вы можете сделать запрос в сети initState и via setState перестроить при получении ответа. Для вкладок можно подготовить и сохранить данные в Родительском виджете. Вы можете найти более подробную информацию в флаттер учебник об управлении Государственной