Использование FutureBuilder в main.dart - PullRequest
0 голосов
/ 01 апреля 2020

Ниже код всегда показывает OnboardingScreen некоторое время (возможно, миллисекунды), после этого отображается MyHomePage. Я уверен, что вы все понимаете, что я пытаюсь сделать. Я использую FutureBuilder, чтобы проверить, есть ли у метода getString данные. В чем моя вина? Или любой другой лучший способ для этого?

saveString() async {
  final prefs = await SharedPreferences.getInstance();
  prefs.setString('firstOpen', '1');
}

Метод getString () всегда возвращает строку.

getString() async {
  final prefs = await SharedPreferences.getInstance();
  String txt = prefs.getString('firstOpen');
  return txt;
}

main.dart

home: new FutureBuilder(
            future: getString(),
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return MyHomePage();
              } else {
                return OnboardingScreen();
              }
            })

Ответы [ 2 ]

1 голос
/ 01 апреля 2020

Обычно я использую другой маршрут, а не FutureBuilder. Поскольку futurebuilder при каждой «горячей» перезагрузке сбрасывает futureBuilder.

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

Snapshot.hasData показывает только возвращенные данные разрешенного будущего.

import 'dart:async';

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

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

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

class SplashScreen extends StatefulWidget {
  @override
  _SplashScreenState createState() => _SplashScreenState();
}

const isOnboardingFinished = 'isOnboardingFinished';

class _SplashScreenState extends State<SplashScreen> {
  Timer timer;
  bool isLoading = true;

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

  Future<void> _checkIfFirstOpen() async {
    final prefs = await SharedPreferences.getInstance();
    var hasOpened = prefs.getBool(isOnboardingFinished) ?? false;

    if (hasOpened) {
      _changePage();
    } else {
      setState(() {
        isLoading = false;
      });
    }
  }

  _changePage() {
    Navigator.of(context).pushReplacement(
      // this is route builder without any animation
      PageRouteBuilder(
        pageBuilder: (context, animation1, animation2) => HomePage(),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return isLoading ? Container() : OnBoarding();
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(child: Text('homePage'));
  }
}

class OnBoarding extends StatelessWidget {
  Future<void> handleClose(BuildContext context) async {
    final prefs = await SharedPreferences.getInstance();
    prefs.setBool(isOnboardingFinished, true);
    Navigator.of(context).pushReplacement(
      MaterialPageRoute(
        builder: (_) => HomePage(),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Center(
        child: RaisedButton(
          onPressed: () => handleClose(context),
          child: Text('finish on bording and never show again'),
        ),
      ),
    );
  }
}
0 голосов
/ 01 апреля 2020

Из документации класса FutureBuilder :

Будущее должно быть получено ранее, например, во время State.initState, State.didUpdateConfig или State.didChangeDependencies. Он не должен создаваться при вызове метода State.build или StatelessWidget.build при создании FutureBuilder. Если будущее создается одновременно с FutureBuilder, то каждый раз, когда родитель FutureBuilder перестраивается, асинхронная задача перезапускается.

Так что вам нужно создать новый виджет Stateful для хранения этого Будущее как государство. С этим состоянием вы можете проверить, какую страницу показывать. Как и предполагалось, вы можете начать будущее в методе initState:

class FirstPage extends StatefulWidget {

  _FirstPageState createState() => _FirstPageState();
}

class _FirstPageState extends State<FirstPage> {
final Future<String> storedFuture;

  @override
  void initState() {
      super.initState();
      storedFuture = getString();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
            future: storedFuture,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return MyHomePage();
              } else {
                return OnboardingScreen();
              }
            });
  }
}

Таким образом, в вашем домашнем свойстве вы можете назвать его FirstPage:

home: FirstPage(),

Ваша ошибка была вызвана getString() из в методе сборки, который будет перезапускать вызов asyn c каждый раз, когда экран перестраивается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...