如何在颤振中使用进度指示器?

我是新手在颤抖,想知道什么是更好的方式添加 CircularProgressIndicator在我的布局。例如,我的登录视图。这个视图有用户名、密码和登录按钮。我确实想创建一个覆盖布局(与 Opacity) ,当加载时,显示进度指示器像我使用的 NativeScript,但我很少困惑如何做,如果这是更好的方式。例如,在 NativeScript 上,我在主布局中添加 IndicatorActivity,并将 busyto 设置为 true 或 false,以便在加载时覆盖所有视图组件。

编辑:

我得出了这样的结论:

    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

我还在适应状态的概念。这个代码在处理颤振时是在预期范围内的吗?

谢谢!

350576 次浏览

对我来说,一个简单的方法是在进行签入过程时在底部显示一个 SnackBar,下面是我的意思的一个例子:

enter image description here

下面是如何设置 SnackBar

Scaffold定义一个全局密钥

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

将其添加到 Scaffold key属性中

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

我的登录按钮 onPressed回调:

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")
);
}

这实际上取决于您想要如何构建布局,我不确定您的想法。

剪辑

您可能希望这样,我已经使用一个堆栈来实现这个结果,只是显示或隐藏我的指示器基于 onPressed

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,),


],));
}


}

在 flutter 中,有几种处理异步操作的方法。

一种懒惰的方法是使用模态。它将阻止用户输入,从而防止任何不必要的操作。 这只需要对代码进行很少的修改,只需要将 _onLoading修改为如下所示:

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();
});
}

最理想的方法是使用 FutureBuilder和一个有状态小部件。 诀窍在于,您可以直接使用 Future<MyUser> user,而不必在状态中使用 boolean loading = false

然后将它作为参数传递给 FutureBuilder,它将提供一些信息,如“ hasData”或完成后的 MyUser实例。

这会导致这样的结果:

@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),
);
}
},
);
}
}

1. 没有插件

    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. 使用插件

检查这个插件 进展

在 pubspec.yaml 文件中添加依赖项

dev_dependencies:
progress_hud:

进口包裹

import 'package:progress_hud/progress_hud.dart';

下面给出了显示和隐藏指示器的示例代码

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;
}
}

我采用了以下方法,它使用了一个简单的模态进度指示器小部件,该小部件可以包装在异步调用期间希望使用的任何模态。

该包中的示例还讨论了如何在进行异步调用以验证表单时处理表单验证(有关此问题的详细信息,请参阅 颤动/问题/9688)。例如,在不离开表单的情况下,这种异步表单验证方法可用于在注册时根据数据库中的现有名称验证新用户名。

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

下面是该软件包提供的示例演示(包含源代码) :

async form validation with modal progress indicator

示例可以适用于其他模态进度指示器行为(如不同的动画,模态中的附加文本等)。

这是我对栈的解决方案

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(),
),
],
));
}
}

可以改用 FutureBuilder 小部件。这需要一个论点,它必须是一个未来。然后您可以使用一个快照,这是登录时异步调用当时的状态,一旦它结束异步函数返回的状态将被更新,未来的构建器将重新构建自己,这样您就可以请求新的状态。

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

这里有一个关于如何构建未来的例子

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

创建一个 bool isLoading并将其设置为 false。在三元操作符的帮助下,当用户点击登录按钮时,将 isLoading的状态设置为 true。您将得到循环加载指示器,而不是登录按钮

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

在单击登录之前,您可以看到屏幕截图 enter image description here

单击登录后 enter image description here

同时,您可以运行登录进程和登录用户。如果用户凭据是错误的,然后再次你将 setStateisLoadingfalse,这样加载指示器将成为不可见的,登录按钮可见的用户。 顺便说一下,代码中使用的主按钮是我自定义的按钮。你可以在 button中使用 OnPressed

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))),
),
),
),

您可以做到这一点,为中心透明的进度指示器

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(),
)
],
);
});
}

步骤1: 创建对话框

   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;
},
);
}

第二步: 宣布死亡

showAlertDialog(context);
await firebaseAuth.signInWithEmailAndPassword(email: email, password: password);
Navigator.pop(context);

使用对话框和登录表单的示例

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),
],)
,),)
),
);
}
}

enter image description here

{
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()));
}
}

以屏幕为中心:

Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [CircularProgressIndicator()])
])

对于您的情况,也许它可以通过使用显示一个圆形指示器的模态来完成。但我建议使用一个简单的插件 https://pub.dev/packages/flutter_easyloading

  • 安装很简单,只要在你的终端上运行这个 flutter pub add flutter_easyloading
  • 把这个输入你的 main.dart 应用程序
import 'package:flutter/material.dart';
import 'package:kunjungi_dokter/pages/welcome.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';  // <- add this


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


class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);


@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: const Welcome(),
builder: EasyLoading.init(), // <- add this
);
}
}
  • 为了显示模态或加载小部件,在我的示例中,我在 Login Screen 的 mya _ Login 函数中显示它:
import 'package:flutter_easyloading/flutter_easyloading.dart';


// ... other code


_login() async {
EasyLoading.show(status: 'loading...', maskType: EasyLoadingMaskType.black); // code to show modal with masking
var data = await LoginAPI.connectToAPI(
emailController.text, passwordController.text);
if (data.isError) {
EasyLoading.showError('Login Error: ' + data.message); // code to show modal without masking and auto close
} else {
await storage.write(key: 'token', value: data.token);
await storage.write(key: 'email', value: emailController.text);
EasyLoading.showSuccess('Login Success!'); // code to show modal without masking and auto close
Navigator.of(context)
.push(MaterialPageRoute(builder: ((context) => const Home())));
}
}
// ... other code
  • 提示,你可以用这个来关闭模式:
EasyLoading.dismiss();

你需要一个图书馆

void onLoading() {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return GFLoader(
type: GFLoaderType.android,
);
},
);
}

然后在需要代码的地方使用这个函数

onLoading;