レバテックフリーランスにて当サイトが紹介されました
幅広い角度からエンジニアのスキルアップのヒントを得られるブログ・サイト
こちらに 「nyan のアプリ開発」 が紹介されました
「レバテックフリーランス」はレバレジーズグループのレバテック株式会社が運営するサイトですが、この当サイトのユーザーであれば「teratail」でお世話になった人も多いかもしれません
当サイトで勉強された後、「レバテックフリーランス」でフリーで稼ぐことも可能でしょう
レバテックフリーランスは登録したフリーランスのエンジニアに
Web系を中心とした案件情報を提案
Web業界・技術に精通した専門のコーディネーターが
案件への参画前から参画後まで、提案やキャリアに関するご相談などのサポートを提供
◎ 充実した特徴・サポート内容 ◎
ON/OFF機能を実装したい時はSwitch Widget が利用できます
トグルスイッチ、つまりONに倒れるか、OFFに倒れるどちらかの値を持ちます
class _TestSwitchState extends State<TestSwitch> { bool flg = true; @override Widget build(BuildContext context) { return Switch( // This bool value toggles the switch. value: flg, onChanged: (bool value) { // This is called when the user toggles the switch. setState(() { flg = value; }); }, ); } }
Swith の基本的なプロパティは
Switchの他のプロパティとして
Switch( value: flg, activeColor: Colors.red, activeTrackColor: Colors.orange, inactiveThumbColor: Colors.black, inactiveTrackColor: Colors.lightGreen, onChanged: (bool value) { setState(() { flg = value; }); }, ),
尚、nullは無効となり、表示上は非活性化されます
以上をまとめてみます
main.dart
import 'package:flutter/material.dart'; void main() { runApp(const MainApp()); } class MainApp extends StatelessWidget { const MainApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData(useMaterial3: true), home: Scaffold( appBar: AppBar(title: const Text('Switch Widget')), body: const Center( child: TestSwitch(), ), ), ); } } class TestSwitch extends StatefulWidget { const TestSwitch({super.key}); @override State<TestSwitch> createState() => _TestSwitchState(); } class _TestSwitchState extends State<TestSwitch> { bool flg = true; @override Widget build(BuildContext context) { return Column( children: <Widget>[ Switch( value: flg, activeColor: Colors.red, activeTrackColor: Colors.orange, inactiveThumbColor: Colors.black, inactiveTrackColor: Colors.lightGreen, onChanged: (bool value) { setState(() { flg = value; }); }, ), Padding( padding: const EdgeInsets.all(16.0), child: Text('Selected Value: $flg'), ), ] ); } }
Switch にテキスト、ListTileを追加したWidgetです
Iconなども設定しました
main.dart
import 'package:flutter/material.dart'; void main() { runApp(const MainApp()); } class MainApp extends StatelessWidget { const MainApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData(useMaterial3: true), home: Scaffold( appBar: AppBar(title: const Text('SwitchListTile')), body: const Center( child: TestSwitch(), ), ), ); } } class TestSwitch extends StatefulWidget { const TestSwitch({super.key}); @override State<TestSwitch> createState() => _TestSwitchState(); } class _TestSwitchState extends State<TestSwitch> { bool flg = true; @override Widget build(BuildContext context) { return Column( children: <Widget>[ SwitchListTile( title: const Text('Toggle switch'), subtitle: const Text('Switch Listtile'), secondary: const Icon(Icons.notifications), value: flg, activeColor: Colors.red, activeTrackColor: Colors.orange, inactiveThumbColor: Colors.black, inactiveTrackColor: Colors.lightGreen, onChanged: (bool value) { setState(() { flg = value; }); }, ), Padding( padding: const EdgeInsets.all(16.0), child: Text('Selected Value: $flg'), ), ] ); } }
References:
Switch class
SwitchListTile class
いわゆるラジオボタンは Radio Wdget となります
複数の選択肢のなかから1つだけ選択する場合に使われるため、Checkbox や Switch Widget とは異なる構成になっています
例えば、Radio Widgetを2つ用意して選択します
Radio Widget を選択すると、
I/flutter ( 9354): radio 2 I/flutter ( 9354): radio 1
のようにDebug Console にプリントします
var selectedRadio = "radio 1"; @override Widget build(BuildContext context) { return Column( children: <Widget>[ Radio<String>( value: "radio 1", groupValue: selectedRadio, onChanged: (value) { setState(() { selectedRadio = value!; debugPrint(selectedRadio); }); }, ), Radio<String>( value: "radio 2", groupValue: selectedRadio, onChanged: (value) { setState(() { selectedRadio = value!; debugPrint(selectedRadio); }); }, ), ], ); }
Radio として<T>のジェネリクスを使い、データ型が抽象化されています
この例ではString型としています
Radio<String>
Radio<String>( value: "radio 1", groupValue: selectedRadio, onChanged: (value) { setState(() { selectedRadio = value!; debugPrint(selectedRadio); }); }, ),
Radio Widget では以下のプロパティの設定が必要です
初期値で、radio 1 を選択していたところから
radio 2 をクリックすると groupValue が “radio 2” となり
raido 1 をクリックすると groupValue が “radio 1” になります
Radio Widget を3つ用意して
ListTileを使ってRadioのテキストを表示して
Option A, B, C をそれぞれ1,2,3として下段に選択したRadioを表示してみます
main.dart
import 'package:flutter/material.dart'; void main() { runApp(const MainApp()); } class MainApp extends StatelessWidget { const MainApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: const Text('Radio button'), ), body: const Center( child: RadioButton(), ), ), ); } } class RadioButton extends StatefulWidget { const RadioButton({super.key}); @override State<RadioButton> createState() => _RadioButtonState(); } class _RadioButtonState extends State<RadioButton> { int _selectedValue = 1; @override Widget build(BuildContext context) { return Column( children: <Widget>[ ListTile( title: const Text('Option A'), leading: Radio<int>( value: 1, groupValue: _selectedValue, onChanged: (int? value) { setState(() { _selectedValue = value!; }); }, ), ), ListTile( title: const Text('Option B'), leading: Radio<int>( value: 2, groupValue: _selectedValue, onChanged: (int? value) { setState(() { _selectedValue = value!; }); }, ), ), ListTile( title: const Text('Option C'), leading: Radio<int>( value: 3, groupValue: _selectedValue, onChanged: (int? value) { setState(() { _selectedValue = value!; }); }, ), ), Padding( padding: const EdgeInsets.all(16.0), child: Text('Selected: $_selectedValue'), ), ], ); } }
Radioはラジオボタンだけですが
RadioにTitleをつけたものがRadioListTileです
ListTileを使った場合と同じようになりますが
多少コードが少なくはなります
main.dart
import 'package:flutter/material.dart'; void main() { runApp(const MainApp()); } class MainApp extends StatelessWidget { const MainApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: const Text('RadioListTile'), ), body: const Center( child: RadioButton(), ), ), ); } } class RadioButton extends StatefulWidget { const RadioButton({super.key}); @override State<RadioButton> createState() => _RadioButtonState(); } class _RadioButtonState extends State<RadioButton> { int _selectedValue = 1; @override Widget build(BuildContext context) { return Column( children: <Widget>[ RadioListTile( title: const Text('Option A'), value: 1, groupValue: _selectedValue, onChanged: (int? value) { setState(() { _selectedValue = value!; }); }, ), RadioListTile( title: const Text('Option B'), value: 2, groupValue: _selectedValue, onChanged: (int? value) { setState(() { _selectedValue = value!; }); }, ), RadioListTile( title: const Text('Option C'), value: 3, groupValue: _selectedValue, onChanged: (int? value) { setState(() { _selectedValue = value!; }); }, ), Padding( padding: const EdgeInsets.all(16.0), child: Text('Selected: $_selectedValue'), ), ], ); } }
References:
Radio class
RadioListTile class
FlutterではAndroidやiOS用にアプリを作成できますが、同時にWebアプリも作れます
これをWordPressを使ってネットで表示するには、
FlutterのWebビルドを生成して、それをWordPressに埋め込むだけです
例えばサンプルアプリとしてよく出てくる、フローティングボタンをクリックすると
数値がインクリメントするものです
以下このアプリを例として、WordPressに埋め込む方法を解説します
Web用にアプリをビルドする必要があり
以下のコマンドを使います
flutter build web
例として、flutter_application_1 のプロジェクトを作成してコマンドを打って見ると
build 以下に web が作成されます
このweb以下に index.html があり、その中の
<base href=”/”>のhrefを以下のように変更します
<!DOCTYPE html> <html> <head> ... <base href="/wp-content/themes/flutter/testapp1/"> ... </body> </html>
この /wp-content/themes/flutter/testapp1/
は、WordPressのサーバーのアップロード先のPathを指しています
あるいは、以下のようにコマンドを打っても同じです
flutter build web "--base-href" "/wp-content/themes/flutter/testapp1/"
アクセスできる場所であればどこでもいいのですが、Backup をとったりする上で
分かりやすいところがいいので wp-content/themes は既にあり管理しやすいのでここを使います
flutterディレクトリーを作成しておきます
FTPを使ってこの web フォルダの名前を変更し
フォルダ毎WordPressのサーバーにアップロードします
今回の例では、testapp1 に変更してアップします
ビルドしたFlutterアプリのHTMLファイルを表示するためには、
WordPressページや投稿の中にiframeを使って埋め込みます
<iframe width="300" height="400" class="aligncenter" src="https://your-site.com/wp-content/themes/flutter/testapp1/index.html" alt=""> </iframe>
このコードを投稿や固定ページの「テキスト」モードで挿入すると、Flutterアプリがページ内に表示されます。
References:
Configuring the URL strategy on the web
Flutter web app initialization
Checkbox でユーザーのチェック状態を受け取れますが、CheckboxLiteTileは
チェックボックスとその横に文字列を表示できるもので、ユーザーに質問し、Yes・Noの回答を受け取るWidgetになります
Checkbox これにtitle、テキストを追加したWidgetです
基本的なコードです
bool isChecked = false; ... CheckboxListTile( title: const Text('CheckboxListTile'), value: isChecked, onChanged: (bool? value) { setState(() { isChecked = value!; }); }, );
左端にTitle、右端にチェックボックスになりました
Checkbox と Title の配置の設定は以下の3種類あり、
ListTileControlAffinity.leading のケースではこのようになります
bool isChecked = false; ... CheckboxListTile( title: const Text('CheckboxListTile'), value: isChecked, onChanged: (bool? value) { setState(() { isChecked = value!; }); }, controlAffinity: ListTileControlAffinity.leading );
CheckboxLiteTile の角丸の枠線を表示させるにはshapeを使います
Container( padding: EdgeInsets.all(10), child: CheckboxListTile( shape: RoundedRectangleBorder( side: const BorderSide(), borderRadius: BorderRadius.circular(5), ), title: const Text('CheckboxListTile'), value: isChecked, onChanged: (bool? value) { setState(() { isChecked = value!; }); }, controlAffinity: ListTileControlAffinity.leading , ), );
Containerに入れてpaddingで余白を設定し、角丸の枠をはめました
以上をまとめてみると
main.dart
import 'package:flutter/material.dart'; void main() { runApp(const MainApp()); } class MainApp extends StatelessWidget { const MainApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: const Text('Checkbox')), body: const Center( child: TestCheckboxListTile(), ), ), ); } } class TestCheckboxListTile extends StatefulWidget { const TestCheckboxListTile({super.key}); @override State<TestCheckboxListTile> createState() => _TestCheckboxListTileState(); } class _TestCheckboxListTileState extends State<TestCheckboxListTile>{ bool isChecked = false; @override Widget build(BuildContext context) { return Container( padding: EdgeInsets.all(10), // margin: const EdgeInsets.all(10), child: CheckboxListTile( shape: RoundedRectangleBorder( side: const BorderSide(), borderRadius: BorderRadius.circular(5), ), title: const Text('CheckboxListTile'), value: isChecked, onChanged: (bool? value) { setState(() { isChecked = value!; }); }, controlAffinity: ListTileControlAffinity.leading , ), ); } }
Android エミュレータで実行した結果です
References:
CheckboxListTile class
CircleCheckboxListTile class
ユーザーからの確認を取るためにチェックを入れるCheckbox Widgetはよく使います
AndroidでもiOSでも同様の機能があります
チェックを入れてユーザーの確認を取得するWidgetです
シンプルに□の升にチェックを入れるための
最低限の設定はこのようになります
bool isChecked = false; ... Checkbox( value: isChecked, onChanged: (bool? value) { setState(() { isChecked = value!; }); }, );
チェックされた状態
value は tristate: null, true, false の3つの状態を持てるとあります
tristateを有効にすると nullの状態、無効を作ることができます
bool? isCheckedTristate = null; ... Checkbox( value: isCheckedTristate, tristate: true, onChanged: (bool? newValue) { setState(() { isCheckedTristate = newValue; }); }, );
null -> false -> true -> null … の順番でTristateが表示させることも可能です
null はーの表示になります
false
true
Checkboxには大きさを設定するプロパティがないので
Transform.scale を使うことでできます
Transform.scale( scale: 4.0, child: Checkbox( value: isChecked, onChanged: (bool? value) { setState(() { isChecked = value!; }); }, ), );
4倍にしてみました
その他の設定として
枠を丸型:shape
枠の太さ:side
チェックの色:checkColor
背景色:fillColor
Checkbox( // 枠を角丸にする、10ではこのケースで円形になる shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(10)), ), // 枠の太さ、0で無し side: const BorderSide(width: 0), // チェックの色 checkColor: Colors.white, // 背景色 fillColor: WidgetStateProperty.resolveWith((states) => Colors.red), ... ),
円形、枠なし、背景色が赤、チェックマークが白になりました
尚、MaterialStateProperty は(v3.19.0-0.3.pre)以降に非推奨になり
代わりにWidgetStatePropertyを使います
チェック状態をTextに表示させて、まとめたコードです
main.dart
import 'package:flutter/material.dart'; void main() { runApp(const MainApp()); } class MainApp extends StatelessWidget { const MainApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: const Text('Checkbox')), body: const Center( child: TestCheckbox(), ), ), ); } } class TestCheckbox extends StatefulWidget { const TestCheckbox({super.key}); @override State<TestCheckbox> createState() => _TestCheckboxState(); } class _TestCheckboxState extends State<TestCheckbox>{ bool isChecked = false; @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( isChecked? 'Checked' : 'not Checked', style: const TextStyle( fontSize: 20, ), ), const SizedBox(height: 30), Transform.scale( scale: 2.0, child: Checkbox( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(10)), ), side: const BorderSide(width: 0), checkColor: Colors.white, fillColor: WidgetStateProperty.resolveWith((states) => Colors.red), value: isChecked, onChanged: (bool? value) { setState(() { isChecked = value!; }); }, ), ), ], ); } }
実行結果です
References;
Checkbox class
CheckboxListTile class
tristate property
Transform class
Transform.scale constructor
Toastのようにユーザーにメッセージを出して認識してもらうためのWidgetとしてSnackBarがあります
Androidでは、そのまま SnackBar というのがありますが、iOSには同じものはありません
似たものとしては、メッセージを表示するToastやUIAlertControllerがあります
最初に、SnackBarはこちらにあるようにScaffoldが重要になります
また、メッセージの表示以外のアクションをさせることも可能です
「No ScaffoldMessenger widget found」のエラーが出る場合は #2 を確認してください
1. Create a Scaffold
return MaterialApp( // ここにScafforldがないとSnackBarが作れない home: Scaffold( appBar: AppBar( title: const Text('SnackBar'), ), body: const SnackBarPage(), ), );
2. Display a SnackBar
snackBar = SnackBar( content: Text('Yay! A SnackBar!'), duration: Duration(seconds: 3), ); // Widget tree から ScaffordMessenger を探して SnackBar を表示させるために使う ScaffoldMessenger.of(context).showSnackBar(snackBar);
ここまでをまとめて
ボタンタップでSnackBarが表示されるコードはこのようになります
import 'package:flutter/material.dart'; void main() { runApp( const MainApp()); } class MainApp extends StatelessWidget { const MainApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( // ここにScafforldがないとSnackBarが作れない home: Scaffold( appBar: AppBar( title: const Text('SnackBar'), ), body: const SnackBarPage(), ), ); } } class SnackBarPage extends StatelessWidget{ const SnackBarPage({super.key}); @override Widget build(BuildContext context) { return Center( child: ElevatedButton( child: const Text('Show SnackBar'), onPressed: () { const snackBar = SnackBar( content: Text('Yay! A SnackBar!'), duration: Duration(seconds: 3), ); ScaffoldMessenger.of(context).showSnackBar(snackBar); }, ), ); } }
スクリーン下にメッセージが表示されました
3. Provide an optional action
SnackBar の特徴として、単なるToastなどとは異なりボタンアクションなどを追加することができます
例えば、ボタンをタップしてSnackBarを表示させ
右端にButtonというテキストボタンを作成して
Textの表示を変えるアクションを入れるとこうなります
var message = 'OK?'; void changeText(){ setState((){ message = 'Yes!'; }); } ... ElevatedButton( child: const Text('Show SnackBar'), onPressed: () { var snackBar = SnackBar( content: Text('Yay! A SnackBar!'), duration: const Duration(seconds: 3), action: SnackBarAction( label: 'Button', onPressed: changeText, ), ); ScaffoldMessenger.of(context).showSnackBar(snackBar); }, ),
以上をまとめて、多少見やすくすると
main.dart
import 'package:flutter/material.dart'; void main() { runApp( const MainApp()); } class MainApp extends StatelessWidget { const MainApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( // ここにScafforldがないとSnackBarが作れない home: Scaffold( appBar: AppBar( title: const Text('SnackBar'), ), body: const SnackBarPage(), ), ); } } class SnackBarPage extends StatefulWidget{ const SnackBarPage({super.key}); @override State<SnackBarPage> createState() => _SnackBarPageState(); } class _SnackBarPageState extends State<SnackBarPage>{ var message = 'OK?'; void changeText(){ setState((){ message = 'Yes!'; }); } @override Widget build(BuildContext context) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( message, style: const TextStyle( fontSize: 30, ) ), const SizedBox(height: 20), ElevatedButton( child: const Text( 'Show SnackBar', style: TextStyle( fontSize: 24, ) ), onPressed: () { var snackBar = SnackBar( content: const Text( 'Yay! A SnackBar!', style: TextStyle( fontSize: 18, ) ), duration: const Duration(seconds: 10), backgroundColor: Colors.blue, action: SnackBarAction( label: 'Button', onPressed: changeText, ), ); ScaffoldMessenger.of(context).showSnackBar(snackBar); }, ), ], ), ); } }
Show SnackBar ボタンをタップして
スクリーン下にSnackBarが表示されます
右端の Button をタップすると
OK? からYes! にTextが変更され
SnackBarのActionが確認されます
SnackBar でこのようなエラーになった場合、
No ScaffoldMessenger widget found.
MainApp widgets require a ScaffoldMessenger widget ancestor.
こちらに説明がありますが
ScaffoldMessenger.of(context).showSnackBar(snackBar);
この context が Scaffold の子Widgetであることを確認しないといけません
References:
SnackBar class
Display a snackbar
ScaffoldMessenger class
FlutterのSliderは、AndroidではSeekBar、iOSではUISliderとかSliderと呼ばれるもので
ツマミを動かすボリュームのようなものです
Sliderのツマミを持って動かすとバーが伸び縮みする挙動で
setState() を使って値が逐次更新されるようにします
double thumb = 50; ... Slider( value: _knobValue, max: 100, onChanged: (double value) { setState(() { thumb = value; }); }, ),
Sliderとしては最低限
value, max, onChanged の設定が必要です
SizedBox の中に入れると、Sliderの横幅サイズを変更できます
SizedBox( width: 250, child: Slider( ... ), ),
250 x 50 のSizedBoxに入れたので横幅が調整されました
色を変更してみます
child: Slider( activeColor: Colors.redAccent, inactiveColor: const Color.fromARGB(255, 244, 196, 200), thumbColor: Colors.purple, ... ),
activeColor:Sliderの選択範囲の色
inactiveColor:未選択範囲の色
thumbColor:ツマミの色
label を追加すると、ツマミ上に数値を表示させるようにできます
そのためには、min, max, division を追加する必要があります
Slider( ..., value: thumb, min: 0, max: 100, divisions: 20, label: thumb.round().toString(), onChanged: (double value) { setState(() { thumb = value; }); }, ),
labelはツマミを掴んだタイミングでその上に数値を表示します
Sliderが細いので太くしてみましょう
SliderTheme を使って trackHeight から設定します
SliderTheme( data: SliderTheme.of(context).copyWith( trackHeight: 20.0, ), child: Slider( ... ), ),
Sliderが太くなりました
まとめて
Sliderの数値をTextで表示させてみます
main.dart
import 'package:flutter/material.dart'; void main() { runApp(const MainApp()); } class MainApp extends StatelessWidget { const MainApp({super.key}); @override Widget build(BuildContext context) { return const MaterialApp( home: TestSlider(), ); } } class TestSlider extends StatefulWidget { const TestSlider({super.key}); @override State<TestSlider> createState() => _TestSliderState(); } class _TestSliderState extends State<TestSlider> { double thumb = 50; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Slider')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget> [ Text( '${thumb.round()}', style: const TextStyle( fontSize: 24.0, ) ), SizedBox( width: 250, child: SliderTheme( data: SliderTheme.of(context).copyWith( trackHeight: 20.0, ), child: Slider( activeColor: Colors.redAccent, inactiveColor: const Color.fromARGB(255, 244, 196, 200), thumbColor: Colors.purple, value: thumb, min: 0, max: 100, divisions: 20, label: thumb.round().toString(), onChanged: (double value) { setState(() { thumb = value; }); }, ), ), ), ], ), ), ); } }
実行させてみます
References;
Slider class
Slider constructor
LinearProgressIndicatorではプログレッシブバーで進捗状況を表示できましたが
円弧で進捗を示せるのが、CircularProgressIndicator です
CircularProgressIndicator を簡単に作成するとこのようになります
ここでvalueをnullにすると無限に回転します
ダウンロードが継続しているがどこまでか推測できない場合に
継続している事を知らせるのに使えます
@override Widget build(BuildContext context) { return const MaterialApp( home: Scaffold( body: Center( child: CircularProgressIndicator(), ), ), ); }
回転アニメーションが継続しています
67%を表示させるには value に0.67 を設定
child: CircularProgressIndicator( value: 0.67, ),
固定されたポジションです
SizedBox でラップすると見やすくなります
@override Widget build(BuildContext context) { return const MaterialApp( home: Scaffold( body: Center( child: SizedBox( width: 200, height: 200, child: CircularProgressIndicator( value: 0.67, ), ), ), ), ); }
縦横 200 x 200 サイズの SizedBox に CircularProgressIndicator を入れ大きさを調整できます
尚、サイズを200×100のような長方形にすると楕円になります
色と背景色は、valueColor と backgroundColor で設定
strokeWidth で太さを調整できます
CircularProgressIndicator( valueColor: AlwaysStoppedAnimation(Colors.redAccent), backgroundColor: Color.fromARGB(255, 244, 196, 200), strokeWidth: 20.0, ... ),
多少太くなって、色が変わりました
このままでは、進捗に応じたダイナミックな動きができないので
value に変数を当てて、何らかの動きで、例えばネットのダウンロードの進捗や
カウント結果などを表示できるようにします
class _ProgressIndicatorState extends State<ProgressIndicator> { var _progress = 0.0; @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: const Text('CircularProgressIndicator')), body: Center( child: SizedBox( width: 100, height: 100, child: CircularProgressIndicator( value: _progress, valueColor: const AlwaysStoppedAnimation(Colors.redAccent), backgroundColor: const Color.fromARGB(255, 244, 196, 200), strokeWidth: 20.0, ), ), ), ), ), ); } }
以降で、これらをまとめてみます
動的な進捗を CircularProgressIndicator で表示させるサンプルコード
Timer を使って1秒毎に 1 つづつ増加して 10 秒まで継続させます
main.dart
import 'package:flutter/material.dart'; import 'dart:async'; void main() { runApp(const MainApp()); } class MainApp extends StatelessWidget { const MainApp({super.key}); @override Widget build(BuildContext context) { return const MaterialApp( home: Scaffold( body: ProgressIndicator(), ), ); } } class ProgressIndicator extends StatefulWidget { const ProgressIndicator({super.key}); @override State<ProgressIndicator> createState() => _ProgressIndicatorState(); } class _ProgressIndicatorState extends State<ProgressIndicator> { double _progress = 0.0; @override void initState() { Timer.periodic(const Duration(seconds: 1), (Timer timer) { setState(() { _progress += 0.1; if (1.0 <= _progress) { _progress = 0.0; timer.cancel(); } }); }); super.initState(); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: const Text('CircularProgressIndicator')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text('${(_progress * 10).round()} sec'), const SizedBox(height: 30), // spacer SizedBox( width: 100, height: 100, child: CircularProgressIndicator( value: _progress, valueColor: const AlwaysStoppedAnimation(Colors.redAccent), backgroundColor: const Color.fromARGB(255, 244, 196, 200), strokeWidth: 20.0, ), ), ], ), ), ), ); } }
コードを実行させると
0から10secまでのタイマーができました
(Restart: 再読み込み)
References:
CircularProgressIndicator class
SizedBox class
Timer class
状態の進捗状況を示すために、プログレッシブバーが使われることがあります
FlutterではLinearProgressIndicatorを利用するとリニアな変化で進捗状況を表示できます
LinearProgressIndicatorで67%を表示させるには
value に0.67 を設定すると簡単にできます
@override Widget build(BuildContext context) { return const MaterialApp( home: Scaffold( body: Center( child: LinearProgressIndicator( value: 0.67, ), ), ), ); }
このような感じでできました
ただ、このままでは使いにくいです
SizedBox でラップすると見やすくなります
@override Widget build(BuildContext context) { return const MaterialApp( home: Scaffold( body: Center( child: SizedBox( width: 300, height: 30, child: LinearProgressIndicator( value: 0.67, ), ), ), ), ); }
縦横 300 x 30 サイズの SizedBox に LinearProgressIndicator を入れるとこのようになり
プログレッシブバーらしくなりました
バーの色と背景色は、valueColorとbackgroundColorで設定できます
LinearProgressIndicator( valueColor: AlwaysStoppedAnimation(Colors.redAccent), backgroundColor: Color.fromARGB(255, 244, 196, 200), ... ),
バーの色が変わりました
角丸にするためには、ClipRRectをかけて
borderRadius を設定するとできます
@override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Center( child: ClipRRect( borderRadius: BorderRadius.circular(12.0), child: const SizedBox( width: 300, height: 30, child: LinearProgressIndicator( valueColor: AlwaysStoppedAnimation(Colors.redAccent), backgroundColor: Color.fromARGB(255, 244, 196, 200), value: 0.67, ), ), ), ), ), ); }
角丸のバーになりました
このままでは、進捗に応じてバーを増減させるダイナミックな動きができないので
value に変数を当てて、何らかの動きで、例えばネットのダウンロードの進捗や
カウント結果などを表示できるようにします
class _ProgressIndicatorState extends State<ProgressIndicator> { var _progress = 0.0; @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: const Text('LinearProgressIndicator')), body: Center( child: ClipRRect( borderRadius: BorderRadius.circular(12.0), child: SizedBox( width: 300, height: 30, child: LinearProgressIndicator( value: _progress, valueColor: const AlwaysStoppedAnimation(Colors.redAccent), backgroundColor: const Color.fromARGB(255, 244, 196, 200), ), ), ), ), ), ); } }
以降で、これらをまとめてみます
動的な進捗を LinearProgressIndicator で表示させるサンプルコード
Timer を使ってバーを1秒毎に 0.01 づつ増加して 1.0 まで、100%まで継続させます
main.dart
import 'package:flutter/material.dart'; import 'dart:async'; void main() { runApp(const MainApp()); } class MainApp extends StatelessWidget { const MainApp({super.key}); @override Widget build(BuildContext context) { return const MaterialApp( home: Scaffold( body: ProgressIndicator(), ), ); } } class ProgressIndicator extends StatefulWidget { const ProgressIndicator({super.key}); @override State<ProgressIndicator> createState() => _ProgressIndicatorState(); } class _ProgressIndicatorState extends State<ProgressIndicator> { double _progress = 0.0; @override void initState() { Timer.periodic(const Duration(seconds: 1), (Timer timer) { setState(() { _progress += 0.01; if (1.0 <= _progress) { _progress = 0.0; timer.cancel(); } }); }); super.initState(); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: const Text('LinearProgressIndicator')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text('${(_progress * 100).round()}%'), ClipRRect( borderRadius: BorderRadius.circular(12.0), child: SizedBox( width: 300, height: 30, child: LinearProgressIndicator( value: _progress, valueColor: const AlwaysStoppedAnimation(Colors.redAccent), backgroundColor: const Color.fromARGB(255, 244, 196, 200), ), ), ), ], ), ), ), ); } }
コードを実行させると
0から100%までバーが増加しました
(Restart: 再読み込み)
References:
LinearProgressIndicator class
SizedBox class
Timer class
ClipRRect class