Ich bin ein Neuling im Flattern und wollte wissen, wie CircularProgressIndicator
ich mein Layout besser hinzufü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 eine Fortschrittsanzeige anzeigt, wie ich sie in NativeScript verwende, aber ich bin wenig verwirrt darüber, wie und auch, wenn es der bessere Weg ist. In NativeScript füge ich beispielsweise IndicatorActivity im Hauptlayout hinzu und setze beschäftigt auf wahr oder falsch, damit beim Laden alle Ansichtskomponenten ü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),
),
);
}
}
Ich passe mich immer noch der Idee von Staaten an. Dieser Code liegt innerhalb der erwarteten bei der Arbeit mit Flattern?
Vielen Dank!
Antworten:
Beim Flattern gibt es einige Möglichkeiten, mit asynchronen Aktionen umzugehen.
Ein fauler Weg, dies zu tun, kann die Verwendung eines Modals sein. Dadurch wird die Benutzereingabe blockiert und unerwünschte Aktionen verhindert. Dies würde nur sehr geringe Änderungen an Ihrem Code erfordern. Ändern Sie einfach Ihre
_onLoading
zu so etwas:void _onLoading() { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return 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 idealste Weg, dies zu tun, ist die Verwendung
FutureBuilder
eines zustandsbehafteten Widgets. Welches ist, was Sie begonnen haben. Der Trick ist, dass Sie anstelle von aboolean loading = false
in Ihrem Bundesstaat direkt a verwenden könnenFuture<MyUser> user
Und übergeben Sie es dann als Argument an
FutureBuilder
, wodurch Sie einige Informationen wie "hasData" oder die Instanz von "Fertig" erhaltenMyUser
.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), ); } }, ); } }
quelle
Navigator.pushNamed("/home")
.Für mich besteht eine gute Möglichkeit darin,
SnackBar
während des Anmeldevorgangs unten ein zu zeigen. Dies ist ein Beispiel für das, was ich meine:Hier erfahren Sie, wie Sie das einrichten
SnackBar
.Definieren Sie einen globalen Schlüssel für Ihre
Scaffold
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
Fügen Sie es Ihrem
Scaffold
key
Attribut hinzureturn new Scaffold( key: _scaffoldKey, .......
onPressed
Rückruf meiner Anmeldeschaltfläche :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 Stapel verwendet, um dieses Ergebnis zu erzielen und nur meinen Indikator basierend auf anzuzeigen oder auszublenden
onPressed
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,), ],)); } }
quelle
_loading
Ändern alle Ansichten neu erstellt. Ist das so?Erstellen Sie einen Bool
isLoading
und setzen Sie ihn auffalse
. Wenn der Benutzer mit Hilfe des ternären Operators auf die Anmeldeschaltfläche klickt, setzen Sie den StatusisLoading
auftrue
. Anstelle der Anmeldeschaltfläche erhalten Sie eine kreisförmige LadeanzeigeisLoading ? new PrimaryButton( key: new Key('login'), text: 'Login', height: 44.0, onPressed: setState((){isLoading = true;})) : Center( child: CircularProgressIndicator(), ),
Sie können Screenshots sehen, wie es aussieht, bevor Sie auf die Anmeldung klicken
Nach dem Anmelden wird geklickt
In der Zwischenzeit können Sie den Anmeldevorgang und den Anmeldebenutzer ausführen. Wenn Benutzerdaten falsch sind dann wieder werden Sie
setState
vonisLoading
zufalse
, so dass sich die Ladeanzeige unsichtbar und Login werden Schaltfläche Benutzer sichtbar. Der im Code verwendete PrimaryButton ist übrigens meine benutzerdefinierte Schaltfläche. Sie können dasselbe mitOnPressed
in tunbutton
.quelle
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üfen Sie dieses Plugin progress_hud
Fügen Sie die Abhängigkeit in die Datei pubspec.yaml ein
Importieren Sie das Paket
import 'package:progress_hud/progress_hud.dart';
Im Folgenden finden Sie einen Beispielcode zum Ein- und Ausblenden des Indikators
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; } }
quelle
Schritt 1: Dialog erstellen
showAlertDialog(BuildContext context){ AlertDialog alert=AlertDialog( content: new Row( children: [ CircularProgressIndicator(), Container(margin: EdgeInsets.only(left: 5),child:Text("Loading" )), ],), ); showDialog(barrierDismissible: false, context:context, builder:(BuildContext context){ return alert; }, ); }
Schritt 2: Nennen Sie es
showAlertDialog(context); await firebaseAuth.signInWithEmailAndPassword(email: email, password: password); Navigator.pop(context);
Beispiel mit Dialog und Anmeldeformular
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:firebase_auth/firebase_auth.dart'; class DynamicLayout extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return new MyWidget(); } } showAlertDialog(BuildContext context){ AlertDialog alert=AlertDialog( content: new Row( children: [ CircularProgressIndicator(), Container(margin: EdgeInsets.only(left: 5),child:Text("Loading" )), ],), ); showDialog(barrierDismissible: false, context:context, builder:(BuildContext context){ return alert; }, ); } class MyWidget extends State<DynamicLayout>{ Color color = Colors.indigoAccent; String title='app'; GlobalKey<FormState> globalKey=GlobalKey<FormState>(); String email,password; login() async{ var currentState= globalKey.currentState; if(currentState.validate()){ currentState.save(); FirebaseAuth firebaseAuth=FirebaseAuth.instance; try { showAlertDialog(context); AuthResult authResult=await firebaseAuth.signInWithEmailAndPassword( email: email, password: password); FirebaseUser user=authResult.user; Navigator.pop(context); }catch(e){ print(e); } }else{ } } @override Widget build(BuildContext context) { return new Scaffold( appBar:AppBar( title: Text("$title"), ) , body: Container(child: Form( key: globalKey, child: Container( padding: EdgeInsets.all(10), child: Column(children: <Widget>[ TextFormField(decoration: InputDecoration(icon: Icon(Icons.email),labelText: 'Email'), // ignore: missing_return validator:(val){ if(val.isEmpty) return 'Please Enter Your Email'; }, onSaved:(val){ email=val; }, ), TextFormField(decoration: InputDecoration(icon: Icon(Icons.lock),labelText: 'Password'), obscureText: true, // ignore: missing_return validator:(val){ if(val.isEmpty) return 'Please Enter Your Password'; }, onSaved:(val){ password=val; }, ), RaisedButton(color: Colors.lightBlue,textColor: Colors.white,child: Text('Login'), onPressed:login), ],) ,),) ), ); } }
quelle
Ich habe den folgenden Ansatz gewählt, bei dem ein einfaches Widget für die Anzeige des modalen Fortschritts verwendet wird, das alles umschließt, was Sie während eines asynchronen Aufrufs modal machen möchten.
Das Beispiel im Paket beschreibt auch, wie die Formularüberprüfung behandelt wird, während asynchrone Aufrufe zur Überprüfung des Formulars ausgeführt werden ( Einzelheiten zu diesem Problem finden Sie unter flutter / issue / 9688 ). Ohne das Formular zu verlassen, kann diese asynchrone Formularüberprüfungsmethode beispielsweise verwendet werden, um einen neuen Benutzernamen bei der Anmeldung anhand vorhandener Namen in einer Datenbank zu überprüfen.
https://pub.dartlang.org/packages/modal_progress_hud
Hier ist die Demo des mit dem Paket gelieferten Beispiels (mit Quellcode):
Das Beispiel könnte an ein anderes Verhalten der modalen Fortschrittsanzeige angepasst werden (z. B. unterschiedliche Animationen, zusätzlicher Text in Modal usw.).
quelle
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(), ), ], )); } }
quelle
Ich schlage vor, dieses Plugin flutter_easyloading zu verwenden
flutter_easyloading ist ein sauberes und leichtes Lade-Widget für die Flutter App, das ohne Kontext einfach zu verwenden ist und iOS und Android unterstützt
Fügen Sie dies der
pubspec.yaml
Datei Ihres Pakets hinzu :dependencies: flutter_easyloading: ^2.0.0
Jetzt können Sie in Ihrem Dart-Code Folgendes verwenden:
import 'package:flutter_easyloading/flutter_easyloading.dart';
Um First zu verwenden, initialisieren Sie
FlutterEasyLoading
inMaterialApp
/CupertinoApp
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import './custom_animation.dart'; import './test.dart'; void main() { runApp(MyApp()); configLoading(); } void configLoading() { EasyLoading.instance ..displayDuration = const Duration(milliseconds: 2000) ..indicatorType = EasyLoadingIndicatorType.fadingCircle ..loadingStyle = EasyLoadingStyle.dark ..indicatorSize = 45.0 ..radius = 10.0 ..progressColor = Colors.yellow ..backgroundColor = Colors.green ..indicatorColor = Colors.yellow ..textColor = Colors.yellow ..maskColor = Colors.blue.withOpacity(0.5) ..userInteractions = true ..customAnimation = CustomAnimation(); }
Verwenden Sie dann gemäß Ihren Anforderungen
import 'package:flutter/material.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:dio/dio.dart'; class TestPage extends StatefulWidget { @override _TestPageState createState() => _TestPageState(); } class _TestPageState extends State<TestPage> { @override void initState() { super.initState(); // EasyLoading.show(); } @override void deactivate() { EasyLoading.dismiss(); super.deactivate(); } void loadData() async { try { EasyLoading.show(); Response response = await Dio().get('https://github.com'); print(response); EasyLoading.dismiss(); } catch (e) { EasyLoading.showError(e.toString()); print(e); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Flutter EasyLoading'), ), body: Center( child: FlatButton( textColor: Colors.blue, child: Text('loadData'), onPressed: () { loadData(); // await Future.delayed(Duration(seconds: 2)); // EasyLoading.show(status: 'loading...'); // await Future.delayed(Duration(seconds: 5)); // EasyLoading.dismiss(); }, ), ), ); } }
quelle
Sie können stattdessen das FutureBuilder-Widget verwenden. Dies erfordert ein Argument, das eine Zukunft sein muss. Anschließend können Sie beim Anmelden einen Snapshot verwenden, der den aktuellen Status des asynchronen Aufrufs darstellt. Sobald dieser beendet ist, wird der Status der asynchronen Funktionsrückgabe aktualisiert und der zukünftige Builder wird sich selbst neu erstellen, 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();}
quelle
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(), ) ], ); }); }
quelle
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))), ), ), ),
quelle
{ isloading? progressIos:Container() progressIos(int i) { return Container( color: i == 1 ? AppColors.liteBlack : i == 2 ? AppColors.darkBlack : i == 3 ? AppColors.pinkBtn : '', child: Center(child: CupertinoActivityIndicator())); } }
quelle