私ははためくの初心者で、レイアウトにCircularProgressIndicator
を追加するより良い方法を知りたいと思っていました。たとえば、ログインビュー。このビューには、ユーザー名、パスワード、およびログインボタンがあります。ロード時に、NativeScriptで使用するような進行状況インジケーターを表示するオーバーレイレイアウト(Opacity
)を作成したかったのですが、それがより良い方法である場合、その方法や方法についても少し混乱しています。たとえば、NativeScriptでは、メインレイアウトにIndicatorActivityを追加し、busyを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),
),
);
}
}
私はまだ状態の考えに適応しています。このコードは、フラッターで作業するときに予想される範囲内ですか?
ありがとう!
フラッターでは、非同期アクションに対処するいくつかの方法があります。
怠zyな方法は、モーダルを使用することです。これにより、ユーザー入力がブロックされ、不要なアクションが防止されます。これには、コードをほとんど変更する必要がありません。 _onLoading
を次のように変更するだけです:
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();
});
}
最も理想的な方法は、FutureBuilder
とステートフルウィジェットを使用することです。それがあなたが始めたことです。トリックは、状態にboolean loading = false
を持たせる代わりに、Future<MyUser> user
を直接使用できることです。
そして、それを引数として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つのきちんとした方法は、サインインプロセスの実行中に下部にSnackBar
を表示することです。これは、私が言っていることの例です。
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")
);
}
レイアウトをどのように構築したいかに大きく依存しますが、何を念頭に置いているのかわかりません。
編集
おそらくこの方法が必要でしょう。Stackを使用してこの結果を達成し、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,),
],));
}
}
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。プラグインあり
このプラグインを確認してください progress_hud
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;
}
}
Bool isLoading
を作成し、false
に設定します。三項演算子の助けを借りて、ユーザーがログインボタンをクリックすると、isLoading
の状態がtrue
に設定されます。ログインボタンの代わりに循環ローディングインジケータが表示されます
isLoading ? new PrimaryButton(
key: new Key('login'),
text: 'Login',
height: 44.0,
onPressed: setState((){isLoading = true;}))
: Center(
child: CircularProgressIndicator(),
),
ログインがクリックされる前のスクリーンショットを見ることができます
その間、ログインプロセスとログインユーザーを実行できます。ユーザー資格情報が間違っている場合は、setState
of isLoading
からfalse
になり、ロードインジケーターが非表示になり、ログインボタンがユーザーに表示されます。ちなみに、コードで使用されるprimaryButtonは私のカスタムボタンです。 OnPressed
のbutton
でも同じことができます。
私は次のアプローチを取りました。これは、非同期呼び出し中にモーダルにするものは何でもラップするシンプルなモーダル進捗インジケータウィジェットを使用します。
パッケージの例では、フォームを検証するための非同期呼び出しを行いながらフォーム検証を処理する方法についても説明しています(この問題の詳細については flutter/issues/9688 を参照してください)。たとえば、フォームを離れることなく、この非同期フォーム検証メソッドを使用して、サインアップ中にデータベース内の既存の名前に対して新しいユーザー名を検証できます。
https://pub.dartlang.org/packages/modal_progress_hud
パッケージに付属するサンプルのデモ(ソースコード付き)は次のとおりです。
例は、他のモーダルプログレスインジケータの動作(異なるアニメーション、モーダルの追加テキストなど)に適合させることができます。
代わりにFutureBuilderウィジェットを使用できます。これはFutureでなければならない引数を取ります。次に、ログイン時に非同期呼び出しの時点の状態であるスナップショットを使用できます。スナップショットが終了すると、非同期関数の状態が戻り、更新されて将来のビルダーが再構築されるため、新しいものを要求できます状態。
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();}
これは私のスタックのソリューションです
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(),
),
],
));
}
}
あなたは中央の透明な進行状況インジケータのためにそれを行うことができます
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(),
)
],
);
});
}
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))),
),
),
),