Флаттер - как сделать рисование виджета только после прочтения общих настроек? - PullRequest
0 голосов
/ 07 октября 2019

Я пытаюсь разобраться с Flutter, создав приложение, которое рисует случайный путь (оно станет более продвинутым, пока не закончится!). Пользователь может изменить количество плиток в пути, и это значение будет сохранено в общих настройках. Если они не изменяют количество плиток, для него устанавливается значение по умолчанию 15.5.

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

Я думаю, что мне нужно использовать FutureBuilder, но я не могу найти много онлайн, чтобы я мог разобраться. Вот что у меня есть (это main.dart):

void main() {
  runApp(MaterialApp(
    title: "Path Maker",
    home: PathMaker(),
  ));
}

class PathMaker extends StatefulWidget {

  int numTiles;

  PathMaker({
        Key key,
        this.numTiles
    }): super(key: key);

  @override
  _PathMakerState createState() => new _PathMakerState();

}

class _PathMakerState extends State<PathMaker> {

  _PathMakerState();

  @override
  void initState() {
    super.initState();

    getPreferences();

  }

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

  void getPreferences() async {
      SharedPreferences prefs = await SharedPreferences.getInstance();
      // Get timing variables
      Set<String> keys = prefs.getKeys();
      if(keys.contains('numTiles')) {
        numTiles = prefs.getInt('numTiles');
      }else {
        numTiles = 15.5;
      }
  }

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Path Maker',
      theme: new ThemeData(
        primarySwatch: Colors.green,
      ),
      home: new Path(widget.numTiles),
    );
  }

}

(path.dart - это другой файл, который определяет класс Path - я не думаю, что он актуален, но могу опубликовать егоесли нужно). Поэтому я хочу, чтобы он вызывал build только после завершения getPreferences. Как мне справиться с этим? Я полностью выбрал неправильный подход?

1 Ответ

1 голос
/ 07 октября 2019

Вы на правильном пути, используя FutureBuilder.

У него есть два параметра: future, который ожидал некоторого Future, который является отсроченным вычислением или потенциальным значением. В вашем случае это Future<SharedPreferences>, возвращаемое из SharedPreferences.getInstance().

Тогда FutureBuilder ожидает builder. Строитель будет вызван с контекстом и AsyncSnapshot, соответствующими типу вашего будущего. AsyncSnapshot - это представление самого последнего взаимодействия с асинхронными вычислениями - в вашем случае получение экземпляра общих предпочтений. AsyncSnapshot.connectionState позволяет нам решить, какой виджет отображать, основываясь на состоянии Future. Пример FutureBuilder подробно описывает различные состояния, а сейчас мы просто рассмотрим ConnectionState.done.

. В состоянии done снимок содержит либо ошибку - чтоздесь мы не будем иметь дело - или data, возвращаемое значение вашего асинхронного вызова функции.

Взяв ваш пример, мы можем создать FutureBuilder<SharedPreferences>. Универсальный тип указывает тип, который будет возвращаться в будущем. Мы назначаем будущее из метода общих предпочтений getInstance для параметра future и закрытия построителя, которое ожидает контекст и асинхронный снимок.

При этом ваш код может выглядеть следующим образом:

class _PathMakerState extends State<PathMaker> {
  _PathMakerState();

  @override
  void initState() {
    super.initState();
  }

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

  void setTiles(SharedPreferences prefs) {
    // Get timing variables
    Set<String> keys = prefs.getKeys();
    if(keys.contains('numTiles')) {
      widget.numTiles = prefs.getDouble('numTiles');
    }else {
      widget.numTiles = 15.5;
    }
  }

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Path Maker',
      theme: new ThemeData(
        primarySwatch: Colors.green,
      ),
      home: FutureBuilder<SharedPreferences>(
        future: SharedPreferences.getInstance(),
        builder: (BuildContext context, AsyncSnapshot<SharedPreferences> snapshot) {
          switch (snapshot.connectionState) {
            case ConnectionState.done:
              // get the tile number from the shared preference instance
              // stored in snapshot.data
              setTiles(snapshot.data);
              return Path(widget.numTiles);
            default:
              return Text("Loading...");
          }
        }
      ),
    );
  }

}
...