Flutter: сохраняющиеся состояния страницы - PullRequest
0 голосов
/ 19 июня 2020

Даже после прочтения этого и этого , я все еще не могу осмыслить сохранение состояний страниц во Flutter.

Я создал пример приложения, у которого есть главная страница с именем MyHomePage и вторая страница с именем SecondPage. MyHomePage имеет плавающую кнопку действия, которая отображает SecondPage через Navigator.push(...). Вторая страница содержит текстовое поле с назначенным контроллером. Я хотел бы сохранить текст текстового поля после того, как я закрою и снова открою SecondPage.

Я пробовал всевозможные комбинации с настройками сегментов, состояний страниц и ключей (вдохновленных ссылками выше), но я не мог заставить его работать.

Также я хотел бы сохранить состояние всей страницы автоматически - без необходимости писать / извлекать каждое отдельное значение вручную (в случае, если у меня много текстовых полей на странице ).

Вот мой код:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

PageStorageKey mykey = new PageStorageKey("testkey");

class MyApp extends StatelessWidget {

  final PageStorageBucket _bucket = new PageStorageBucket();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: PageStorage(
        bucket: _bucket,
        child: MyHomePage(),
      )
    );
  }
}

class MyHomePage extends StatefulWidget {

  MyHomePage({Key key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("State demo"),
      ),
      body: Center(
        child: Column(
          children: <Widget>[
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _openSecondPage,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }

  _openSecondPage() {
    Navigator.push(context, new MaterialPageRoute(builder: (context) => new SecondPage()));
  }

}

class SecondPage extends StatefulWidget {
  @override
  _SecondPageState createState() => _SecondPageState();
}

class _SecondPageState extends State<SecondPage> {

  final _aController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second page"),
      ),
      body: Center(
        child: TextField(
          controller: _aController,
          key: mykey,
          autofocus: true,
        ),
      )
    );
  }
}

EDIT:

Основываясь на ответе Аджая, я смог значительно упростить рабочий код . Оказывается, что для сохранения состояний виджета вручную все, что вам нужно, это экземпляр PageStorageBucket в сочетании с ValueKey экземплярами.

Вот изменения, которые я внес в код Ajay:

  • Удален плагин after_layout (достаточно метода initState).
  • Удален глобальный экземпляр PageStorageKey (заменен локальным экземпляром ValueKey на странице, которая должна его использовать).
  • Удален глобальный экземпляр PageStorageBucket и заменен последним экземпляром из MyApp, который передается на страницы, которым он нужен, через атрибуты конструктора.
    • Удалено PageStorage из дерева компонентов.

Вот полученный код (простейшая рабочая форма):

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {

  final bucket = PageStorageBucket();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: MyHomePage(bucket: bucket,),
    );
  }
}

class MyHomePage extends StatefulWidget {

  final PageStorageBucket bucket;

  const MyHomePage({Key key, this.bucket}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("State demo"),
      ),
      body: Center(
        child: Column(
          children: <Widget>[],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _openSecondPage,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }

  _openSecondPage() {
    Navigator.push(
        context, new MaterialPageRoute(builder: (context) => new SecondPage(bucket: widget.bucket,)));
  }
}

class SecondPage extends StatefulWidget {

  final PageStorageBucket bucket;

  const SecondPage({Key key, this.bucket}) : super(key: key);

  @override
  _SecondPageState createState() => _SecondPageState();
}

class _SecondPageState extends State<SecondPage> {

  static const KEY_A = ValueKey("secondPage.A");

  final _aController = TextEditingController();

  @override
  void initState() {
    super.initState();
    _aController.addListener(_updateValue);

    String value = widget.bucket.readState(context, identifier: KEY_A) ?? "";
    _aController.text = value;
  }

  _updateValue() {
    widget.bucket.writeState(context, _aController.text, identifier: KEY_A);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second page"),
      ),
      body: Center(
        child: TextField(
          controller: _aController,
          autofocus: true,
        ),
      ),
    );
  }
}

1 Ответ

1 голос
/ 19 июня 2020

вам также необходимо читать и записывать состояние.

demo

Ознакомьтесь с приведенным ниже кодом.

Примечание: я использовал after_layout для инициализации текстового контроллера.

import 'package:after_layout/after_layout.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

PageStorageKey mykey = new PageStorageKey("testkey");
final PageStorageBucket _bucket = new PageStorageBucket();

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: PageStorage(
          bucket: _bucket,
          child: MyHomePage(),
        ));
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("State demo"),
      ),
      body: Center(
        child: Column(
          children: <Widget>[],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _openSecondPage,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }

  _openSecondPage() {
    Navigator.push(
        context, new MaterialPageRoute(builder: (context) => new SecondPage()));
  }
}

class SecondPage extends StatefulWidget {
  @override
  _SecondPageState createState() => _SecondPageState();
}

class _SecondPageState extends State<SecondPage>
    with AfterLayoutMixin<SecondPage> {
  final _aController = TextEditingController();

  @override
  void initState() {
    super.initState();
    _aController.addListener(_updateValue);
  }

  @override
  void afterFirstLayout(BuildContext context) {
    String value =
        _bucket.readState(context, identifier: ValueKey(mykey)) ?? "";
    print(value);
    _aController.text = value;
  }

  _updateValue() {
    _bucket.writeState(context, _aController.text, identifier: ValueKey(mykey));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second page"),
      ),
      body: Center(
        child: TextField(
          controller: _aController,
          key: mykey,
          autofocus: true,
        ),
      ),
    );
  }
}
...