webentwicklung-frage-antwort-db.com.de

Wie arbeiten Sie mit der Fortschrittsanzeige im Flattern

Ich bin ein Neuling im Flattern und wollte wissen, wie ich CircularProgressIndicator besser in mein Layout einfügen kann. Zum Beispiel meine Login-Ansicht. Diese Ansicht hat Benutzername, Passwort und Login-Button. Ich wollte ein Overlay-Layout (mit Opacity) erstellen, das beim Laden den Fortschrittsindikator anzeigt, wie ich ihn in NativeScript verwende, aber ich bin wenig verwirrt mit der Vorgehensweise und auch, wenn dies der bessere Weg ist. Bei NativeScript füge ich beispielsweise IndicatorActivity im Hauptlayout hinzu und setze busy auf true oder false, damit alle Ansichtskomponenten beim Laden überlagert werden.

Bearbeiten:

Ich konnte dieses Ergebnis erreichen:

    void main() {
      runApp(new MyApp());
    }

    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @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> {
      bool _loading = false;

      void _onLoading() {
        setState(() {
          _loading = true;
          new Future.delayed(new Duration(seconds: 3), _login);
        });
      }


      Future _login() async{
        setState((){
          _loading = false;
        });
      }

      @override
      Widget build(BuildContext context) {


          var body = new Column(
              children: <Widget>[
                new Container(
                  height: 40.0,
                  padding: const EdgeInsets.all(10.0),
                  margin: const EdgeInsets.fromLTRB(15.0, 150.0, 15.0, 0.0),
                  decoration: new BoxDecoration(
                    color: Colors.white,
                  ),
                  child: new TextField(
                    decoration: new InputDecoration.collapsed(hintText: "username"),
                  ),
                ),
                new Container(
                  height: 40.0,
                  padding: const EdgeInsets.all(10.0),
                  margin: const EdgeInsets.all(15.0),
                  decoration: new BoxDecoration(
                    color: Colors.white,
                  ),
                  child: new TextField(
                    decoration: new InputDecoration.collapsed(hintText: "password"),
                  ),
                ),
              ],
            );


          var bodyProgress = new Container(
            child: new Stack(
              children: <Widget>[
                body,
                new Container(
                  alignment: AlignmentDirectional.center,
                  decoration: new BoxDecoration(
                    color: Colors.white70,
                  ),
                  child: new Container(
                    decoration: new BoxDecoration(
                      color: Colors.blue[200],
                      borderRadius: new BorderRadius.circular(10.0)
                    ),
                    width: 300.0,
                    height: 200.0,
                    alignment: AlignmentDirectional.center,
                    child: new Column(
                      crossAxisAlignment: CrossAxisAlignment.center,
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: <Widget>[
                        new Center(
                          child: new SizedBox(
                            height: 50.0,
                            width: 50.0,
                            child: new CircularProgressIndicator(
                              value: null,
                              strokeWidth: 7.0,
                            ),
                          ),
                        ),
                        new Container(
                          margin: const EdgeInsets.only(top: 25.0),
                          child: new Center(
                            child: new Text(
                              "loading.. wait...",
                              style: new TextStyle(
                                color: Colors.white
                              ),
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
              ],
            ),
          );

          return new Scaffold(
            appBar: new AppBar(
              title: new Text(widget.title),
            ),
            body: new Container(
              decoration: new BoxDecoration(
                color: Colors.blue[200]
              ),
              child: _loading ? bodyProgress : body
            ),
            floatingActionButton: new FloatingActionButton(
              onPressed: _onLoading,
              tooltip: 'Loading',
              child: new Icon(Icons.check),
            ),
          );
      }
    }

app screen result

Ich passe mich immer noch an die Idee von Staaten an. Dieser Code liegt im erwarteten Bereich, wenn mit Flattern gearbeitet wird?

Vielen Dank!

19
Ricardo Bocchi

Beim Flattern gibt es einige Möglichkeiten, mit asynchronen Aktionen umzugehen.

Ein fauler Weg, dies zu tun, kann ein Modal sein. Dadurch werden die Benutzereingaben blockiert und somit unerwünschte Aktionen verhindert. Dies würde nur sehr wenig Änderungen am Code erfordern. Ändern Sie einfach Ihren _onLoading in etwa wie folgt:

void _onLoading() {
  showDialog(
    context: context,
    barrierDismissible: false,
    child: new Dialog(
      child: new Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          new CircularProgressIndicator(),
          new Text("Loading"),
        ],
      ),
    ),
  );
  new Future.delayed(new Duration(seconds: 3), () {
    Navigator.pop(context); //pop dialog
    _login();
  });
}

Der beste Weg, dies zu tun, ist FutureBuilder und ein stateful-Widget. Welches ist, was Sie angefangen haben .. Der Trick ist, dass Sie, anstatt einen boolean loading = false in Ihrem Staat zu haben, einen Future<MyUser> user direkt verwenden können.

Und dann übergeben Sie es als Argument an FutureBuilder, das Ihnen Informationen wie "hasData" oder die Instanz von MyUser liefert, wenn Sie fertig sind.

Dies würde zu so etwas führen:

@immutable
class MyUser {
  final String name;

  MyUser(this.name);
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      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> {
  Future<MyUser> user;

  void _logIn() {
    setState(() {
      user = new Future.delayed(const Duration(seconds: 3), () {
        return new MyUser("Toto");
      });
    });
  }

  Widget _buildForm(AsyncSnapshot<MyUser> snapshot) {
    var floatBtn = new RaisedButton(
      onPressed:
          snapshot.connectionState == ConnectionState.none ? _logIn : null,
      child: new Icon(Icons.save),
    );
    var action =
        snapshot.connectionState != ConnectionState.none && !snapshot.hasData
            ? new Stack(
                alignment: FractionalOffset.center,
                children: <Widget>[
                  floatBtn,
                  new CircularProgressIndicator(
                    backgroundColor: Colors.red,
                  ),
                ],
              )
            : floatBtn;

    return new ListView(
      padding: const EdgeInsets.all(15.0),
        children: <Widget>[
          new ListTile(
            title: new TextField(),
          ),
          new ListTile(
            title: new TextField(obscureText: true),
          ),
          new Center(child: action)
        ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return new FutureBuilder(
      future: user,
      builder: (context, AsyncSnapshot<MyUser> snapshot) {
        if (snapshot.hasData) {
          return new Scaffold(
            appBar: new AppBar(
              title: new Text("Hello ${snapshot.data.name}"),
            ),
          );
        } else {
          return new Scaffold(
            appBar: new AppBar(
              title: new Text("Connection"),
            ),
            body: _buildForm(snapshot),
          );
        }
      },
    );
  }
}
24
Rémi Rousselet

Für mich ist es ein guter Weg, dies zu tun, wenn während des Anmeldeprozesses unten ein SnackBar angezeigt wird. Dies ist ein Beispiel für das, was ich meine:

 enter image description here

So richten Sie das SnackBar ein.

Definieren Sie einen globalen Schlüssel für Ihre Scaffold

final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();

Fügen Sie es Ihrem Scaffoldkey-Attribut hinzu

return new Scaffold(
      key: _scaffoldKey,
.......

Mein SignIn-Button onPressed-Rückruf:

onPressed: () {
                  _scaffoldKey.currentState.showSnackBar(
                      new SnackBar(duration: new Duration(seconds: 4), content:
                      new Row(
                        children: <Widget>[
                          new CircularProgressIndicator(),
                          new Text("  Signing-In...")
                        ],
                      ),
                      ));
                  _handleSignIn()
                      .whenComplete(() =>
                      Navigator.of(context).pushNamed("/Home")
                  );
                }

Es hängt wirklich davon ab, wie Sie Ihr Layout erstellen möchten, und ich bin mir nicht sicher, was Sie vorhaben.

Bearbeiten

Sie möchten es wahrscheinlich so, ich habe einen Stack verwendet, um dieses Ergebnis zu erzielen, und einfach meinen Indikator basierend auf onPressed ein- oder ausblenden.

 enter image description here

class TestSignInView extends StatefulWidget {
  @override
  _TestSignInViewState createState() => new _TestSignInViewState();
}


class _TestSignInViewState extends State<TestSignInView> {
  bool _load = false;
  @override
  Widget build(BuildContext context) {
    Widget loadingIndicator =_load? new Container(
      color: Colors.grey[300],
      width: 70.0,
      height: 70.0,
      child: new Padding(padding: const EdgeInsets.all(5.0),child: new Center(child: new CircularProgressIndicator())),
    ):new Container();
    return new Scaffold(
      backgroundColor: Colors.white,
      body:  new Stack(children: <Widget>[new Padding(
        padding: const EdgeInsets.symmetric(vertical: 50.0, horizontal: 20.0),
        child: new ListView(

          children: <Widget>[
            new Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center
              ,children: <Widget>[
            new TextField(),
            new TextField(),

            new FlatButton(color:Colors.blue,child: new Text('Sign In'),
                onPressed: () {
              setState((){
                _load=true;
              });

                  //Navigator.of(context).Push(new MaterialPageRoute(builder: (_)=>new HomeTest()));
                }
            ),

            ],),],
        ),),
        new Align(child: loadingIndicator,alignment: FractionalOffset.center,),

      ],));
  }

}
13
aziza

1. Ohne Plugin

    class IndiSampleState extends State<ProgHudPage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text('Demo'),
        ),
        body: Center(
          child: RaisedButton(
            color: Colors.blueAccent,
            child: Text('Login'),
            onPressed: () async {
              showDialog(
                  context: context,
                  builder: (BuildContext context) {
                    return Center(child: CircularProgressIndicator(),);
                  });
              await loginAction();
              Navigator.pop(context);
            },
          ),
        ));
  }

  Future<bool> loginAction() async {
    //replace the below line of code with your login request
    await new Future.delayed(const Duration(seconds: 2));
    return true;
  }
}

2. Mit Plugin

überprüfe dieses Plugin progress_hud

fügen Sie die Abhängigkeit in der Datei pubspec.yaml hinzu

dev_dependencies:
  progress_hud: 

das Paket importieren

import 'package:progress_hud/progress_hud.Dart';

Im Folgenden finden Sie Beispielcode, um den Indikator ein- und auszublenden

class ProgHudPage extends StatefulWidget {
  @override
  _ProgHudPageState createState() => _ProgHudPageState();
}

class _ProgHudPageState extends State<ProgHudPage> {
  ProgressHUD _progressHUD;
  @override
  void initState() {
    _progressHUD = new ProgressHUD(
      backgroundColor: Colors.black12,
      color: Colors.white,
      containerColor: Colors.blue,
      borderRadius: 5.0,
      loading: false,
      text: 'Loading...',
    );
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text('ProgressHUD Demo'),
        ),
        body: new Stack(
          children: <Widget>[
            _progressHUD,
            new Positioned(
                child: RaisedButton(
                  color: Colors.blueAccent,
                  child: Text('Login'),
                  onPressed: () async{
                    _progressHUD.state.show();
                    await loginAction();
                    _progressHUD.state.dismiss();
                  },
                ),
                bottom: 30.0,
                right: 10.0)
          ],
        ));
  }

  Future<bool> loginAction()async{
    //replace the below line of code with your login request
    await new Future.delayed(const Duration(seconds: 2));
    return true;
  }
}
7
Shyju M

Erstellen Sie ein bool isLoading und setzen Sie es auf false. Wenn der Benutzer auf die Schaltfläche Anmelden klickt, setzen Sie den Status von isLoading auf true. Anstelle des Login-Buttons erhalten Sie eine Umlaufanzeige

 isLoading ? new PrimaryButton(
                      key: new Key('login'),
                      text: 'Login',
                      height: 44.0,
                      onPressed: SetState((){isLoading = ture;}))
                  : Center(
                      child: CircularProgressIndicator(),
                    ),

Sie können Screenshots sehen, wie es aussieht, während Sie auf das Login klicken  enter image description here

Nach dem Login geklickt  enter image description here

In der Zwischenzeit können Sie den Anmeldeprozess und den Anmeldebenutzer ausführen. Wenn die Anmeldeinformationen der Benutzer falsch sind, werden Sie wieder setState von isLoading in false umbenannt, so dass der Ladeindikator unsichtbar wird und die Anmeldeschaltfläche für den Benutzer sichtbar ist . Im Code ist primaryButton übrigens meine benutzerdefinierte Schaltfläche. Sie können dasselbe mit OnPressed in button tun.

5

Ich habe den folgenden Ansatz gewählt, der ein einfaches modales Fortschrittsanzeiger-Widget verwendet, das alles umschließt, was Sie während eines asynchronen Aufrufs modal machen möchten. 

In dem Beispiel im Paket wird auch beschrieben, wie die Formularüberprüfung bei asynchronen Aufrufen zur Überprüfung des Formulars ausgeführt wird (Einzelheiten hierzu finden Sie unter flutter/issues/9688 ). Ohne das Formular zu verlassen, kann diese async-Formularüberprüfungsmethode beispielsweise verwendet werden, um einen neuen Benutzernamen anhand der vorhandenen Namen in einer Datenbank zu überprüfen, während er sich anmeldet.

https://pub.dartlang.org/packages/modal_progress_hud

Hier ist die Demo des mit dem Paket gelieferten Beispiels (mit Quellcode):

 async form validation with modal progress indicator

Das Beispiel könnte an das Verhalten anderer modaler Statusanzeigen angepasst werden (z. B. verschiedene Animationen, zusätzlicher Text in modal usw.).

3
mmccabe

Sie können dies für die mittlere transparente Fortschrittsanzeige tun

Future<Null> _submitDialog(BuildContext context) async {
  return await showDialog<Null>(
      context: context,
      barrierDismissible: false,
      builder: (BuildContext context) {
        return SimpleDialog(
          elevation: 0.0,
          backgroundColor: Colors.transparent,
          children: <Widget>[
            Center(
              child: CircularProgressIndicator(),
            )
          ],
        );
      });
}
0
Cenk YAGMUR

Sie können stattdessen das FutureBuilder-Widget verwenden. Dies erfordert ein Argument, das eine Zukunft sein muss. Dann können Sie eine Momentaufnahme verwenden, die sich zum Zeitpunkt des asynchronen Aufrufs beim Anmelden befindet. Sobald der Status der async-Funktion beendet ist, wird return aktualisiert und der zukünftige Builder wird sich selbst neu aufbauen, sodass Sie nach dem neuen fragen können Zustand.

FutureBuilder(
  future:  myFutureFunction(),
  builder: (context, AsyncSnapshot<List<item>> snapshot) {
    if (!snapshot.hasData) {
      return Center(
        child: CircularProgressIndicator(),
      );
    } else {
     //Send the user to the next page.
  },
);

Hier haben Sie ein Beispiel, wie Sie eine Zukunft aufbauen können

Future<void> myFutureFunction() async{
 await callToApi();}
0
Matias

Dies ist meine Lösung mit Stack

import 'package:flutter/material.Dart';
import 'package:shared_preferences/shared_preferences.Dart';
import 'Dart:async';

final themeColor = new Color(0xfff5a623);
final primaryColor = new Color(0xff203152);
final greyColor = new Color(0xffaeaeae);
final greyColor2 = new Color(0xffE8E8E8);

class LoadindScreen extends StatefulWidget {
  LoadindScreen({Key key, this.title}) : super(key: key);
  final String title;
  @override
  LoginScreenState createState() => new LoginScreenState();
}

class LoginScreenState extends State<LoadindScreen> {
  SharedPreferences prefs;

  bool isLoading = false;

  Future<Null> handleSignIn() async {
    setState(() {
      isLoading = true;
    });
    prefs = await SharedPreferences.getInstance();
    var isLoadingFuture = Future.delayed(const Duration(seconds: 3), () {
      return false;
    });
    isLoadingFuture.then((response) {
      setState(() {
        isLoading = response;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(
            widget.title,
            style: TextStyle(color: primaryColor, fontWeight: FontWeight.bold),
          ),
          centerTitle: true,
        ),
        body: Stack(
          children: <Widget>[
            Center(
              child: FlatButton(
                  onPressed: handleSignIn,
                  child: Text(
                    'SIGN IN WITH GOOGLE',
                    style: TextStyle(fontSize: 16.0),
                  ),
                  color: Color(0xffdd4b39),
                  highlightColor: Color(0xffff7f7f),
                  splashColor: Colors.transparent,
                  textColor: Colors.white,
                  padding: EdgeInsets.fromLTRB(30.0, 15.0, 30.0, 15.0)),
            ),

            // Loading
            Positioned(
              child: isLoading
                  ? Container(
                      child: Center(
                        child: CircularProgressIndicator(
                          valueColor: AlwaysStoppedAnimation<Color>(themeColor),
                        ),
                      ),
                      color: Colors.white.withOpacity(0.8),
                    )
                  : Container(),
            ),
          ],
        ));
  }
}
0
kokemomuke
class Loader extends StatefulWidget {
      @override
      State createState() => LoaderState();
    }

    class LoaderState extends State<Loader> with SingleTickerProviderStateMixin {
      AnimationController controller;
      Animation<double> animation;

      @override
      void initState() {
        super.initState();
        controller = AnimationController(
            duration: Duration(milliseconds: 1200), vsync: this);
        animation = CurvedAnimation(parent: controller, curve: Curves.elasticOut);
        animation.addListener(() {
          this.setState(() {});
        });
        animation.addStatusListener((AnimationStatus status) {});
        controller.repeat();
      }

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

      @override
      Widget build(BuildContext context) {
        return Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Container(
              color: Colors.blue,
              height: 3.0,
              width: animation.value * 100.0,
            ),
            Padding(
              padding: EdgeInsets.only(bottom: 5.0),
            ),
            Container(
              color: Colors.blue[300],
              height: 3.0,
              width: animation.value * 75.0,
            ),
            Padding(
              padding: EdgeInsets.only(bottom: 5.0),
            ),
            Container(
              color: Colors.blue,
              height: 3.0,
              width: animation.value * 50.0,
            )
          ],
        );
      }
    }


    Expanded(
                        child: Padding(
                          padding:
                              EdgeInsets.only(left: 20.0, right: 5.0, top:20.0),
                          child: GestureDetector(
                            onTap: () {
                              Navigator.Push(
                                  context,
                                  MaterialPageRoute(
                                      builder: (context) => FirstScreen()));
                            },
                            child: Container(
                                alignment: Alignment.center,
                                height: 45.0,
                                decoration: BoxDecoration(
                                    color: Color(0xFF1976D2),
                                    borderRadius: BorderRadius.circular(9.0)),
                                child: Text('Login',
                                    style: TextStyle(
                                        fontSize: 20.0, color: Colors.white))),
                          ),
                        ),
                      ),
0
Max Zubko