nyan のアプリ開発 https://akira-watson.com Androidアプリ、作り方、ベーシックサンプル集 Thu, 09 Jan 2025 04:39:05 +0000 ja hourly 1 https://wordpress.org/?v=6.7.2 https://akira-watson.com/wp-content/uploads/2024/07/cropped-cat_3-32x32.png nyan のアプリ開発 https://akira-watson.com 32 32 お知らせ 2025.01.09 https://akira-watson.com/other/news_202501.html?utm_source=rss&utm_medium=rss&utm_campaign=news_202501 <![CDATA[nyan]]> Thu, 09 Jan 2025 04:26:38 +0000 <![CDATA[other]]> https://akira-watson.com/?p=14643 <![CDATA[  レバテックフリーランスにて当サイトが紹介されました   お知らせ   幅広い角度からエンジニアのスキルアップのヒントを得られるブログ・サイト こちらに 「nyan のアプリ開発」 が紹介されました...]]> <![CDATA[

 

レバテックフリーランスにて当サイトが紹介されました
 

お知らせ

 

幅広い角度からエンジニアのスキルアップのヒントを得られるブログ・サイト

こちらに 「nyan のアプリ開発」 が紹介されました

レバテックフリーランス」はレバレジーズグループのレバテック株式会社が運営するサイトですが、この当サイトのユーザーであれば「teratail」でお世話になった人も多いかもしれません

当サイトで勉強された後、「レバテックフリーランス」でフリーで稼ぐことも可能でしょう

レバテックフリーランス

 
レバテックフリーランスは登録したフリーランスのエンジニアに
Web系を中心とした案件情報を提案

Web業界・技術に精通した専門のコーディネーターが
案件への参画前から参画後まで、提案やキャリアに関するご相談などのサポートを提供

 

◎ 充実した特徴・サポート内容 ◎

  • 業界最大級の案件を保有
  • 業界トップクラスの高単価報酬、低マージン
  • コーディネータの実績、丁寧な対応で無理な案件のご提案はしません
  • 案件参画中のフォローの充実
    • 人材担当、企業担当の他に、フォロー専門の担当あり
  • 早期の契約更新確認
    • 契約の途切れの不安を解消するよう、徹底した早期確認を実施
  • 参画者優待サービス
    • 宿泊施設やスポーツクラブ、映画館の優待チケットの特典が満載
]]>
[Flutter] Switch Widget はON/OFFのトグルスイッチ https://akira-watson.com/flutter/switch.html?utm_source=rss&utm_medium=rss&utm_campaign=switch <![CDATA[nyan]]> Fri, 27 Sep 2024 01:07:24 +0000 <![CDATA[Flutter]]> https://akira-watson.com/?p=14594 <![CDATA[ON/OFF機能を実装したい時はSwitch Widget が利用できます Switch Widget   トグルスイッチ、つまりONに倒れるか、OFFに倒れるどちらかの値を持ちます [crayon-67f3330c4b...]]> <![CDATA[

ON/OFF機能を実装したい時はSwitch Widget が利用できます

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 の基本的なプロパティは

  • value: ブール値、オンではtrue、オフでは false を表します
  • onChanged: コールバック関数で、オンからオフ、あるいはオフからオンに更新される

 

 

Switchの他のプロパティとして

Switch(
  value: flg,
  activeColor: Colors.red,
  activeTrackColor: Colors.orange,
  inactiveThumbColor: Colors.black,
  inactiveTrackColor: Colors.lightGreen,
  onChanged: (bool value) {
    setState(() {
      flg = value;
    });
  },
),

  • activeColor: Switch ON でのツマミの色
  • activeThumbImage: ツマミをアイコンにできる
  • activeTrackColor: ONでのトラック背景色
  • inactiveThumbColor: Switch OFF でのツマミの色
  • inactiveTrackColor: OFFでのトラック背景色

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

 

 

実行結果

 

 

 

 

SwitchListTile

 
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

]]>
[Flutter] Radio グループから1つ選択するWidget https://akira-watson.com/flutter/radio.html?utm_source=rss&utm_medium=rss&utm_campaign=radio <![CDATA[nyan]]> Wed, 25 Sep 2024 12:13:16 +0000 <![CDATA[Flutter]]> https://akira-watson.com/?p=14629 <![CDATA[いわゆるラジオボタンは Radio Wdget となります 複数の選択肢のなかから1つだけ選択する場合に使われるため、Checkbox や Switch Widget とは異なる構成になっています Radio Widge...]]> <![CDATA[

いわゆるラジオボタンは Radio Wdget となります
複数の選択肢のなかから1つだけ選択する場合に使われるため、Checkbox や Switch Widget とは異なる構成になっています

Radio 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 では以下のプロパティの設定が必要です

  • value: このRadio Widet の値
  • groupValue: グループ内の Radio Widget で現行選択されている値
  • onChanged: 新しく選択、setState()でgroupValueの値が更新される

初期値で、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'),
        ),
      ],
    );
  }
}

 

 

実行結果

 

 

 

RadioListTile

 
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] Webアプリを WordPressに埋め込む方法 https://akira-watson.com/flutter/we-app-snippets.html?utm_source=rss&utm_medium=rss&utm_campaign=we-app-snippets <![CDATA[nyan]]> Sat, 21 Sep 2024 08:15:01 +0000 <![CDATA[Flutter]]> https://akira-watson.com/?p=14623 <![CDATA[FlutterではAndroidやiOS用にアプリを作成できますが、同時にWebアプリも作れます これをWordPressを使ってネットで表示するには、 FlutterのWebビルドを生成して、それをWordPressに...]]> <![CDATA[

FlutterではAndroidやiOS用にアプリを作成できますが、同時にWebアプリも作れます
これをWordPressを使ってネットで表示するには、
FlutterのWebビルドを生成して、それをWordPressに埋め込むだけです

Webアプリ

 
例えばサンプルアプリとしてよく出てくる、フローティングボタンをクリックすると
数値がインクリメントするものです

以下このアプリを例として、WordPressに埋め込む方法を解説します

 

 

FlutterでWebアプリをビルド

 
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 に変更してアップします

 

WordPressに埋め込み

 
ビルドした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アプリがページ内に表示されます。

補足

  • WordPressに「Code Snippet」などのショートコードプラグインをインストールして、プラグインを使用して、FlutterアプリのHTMLコードをショートコードとしWordPressの投稿やページの中に挿入することで、Flutterアプリを表示することも可能です
  • Flutter Webアプリのサイズによっては、ページの読み込み速度に影響が出ることがあります。可能であれば、キャッシュプラグインやCDNを使用して最適化しましょう。
  • モバイルでの表示にも注意し、レスポンシブ対応ができているかを確認してください。

 

 

References:
Configuring the URL strategy on the web
Flutter web app initialization
 

]]>
[Flutter] CheckboxListTile ラベル付きCheckbox https://akira-watson.com/flutter/checkboxlisttile.html?utm_source=rss&utm_medium=rss&utm_campaign=checkboxlisttile <![CDATA[nyan]]> Tue, 10 Sep 2024 06:49:34 +0000 <![CDATA[Flutter]]> https://akira-watson.com/?p=14587 <![CDATA[Checkbox でユーザーのチェック状態を受け取れますが、CheckboxLiteTileは チェックボックスとその横に文字列を表示できるもので、ユーザーに質問し、Yes・Noの回答を受け取るWidgetになります C...]]> <![CDATA[

Checkbox でユーザーのチェック状態を受け取れますが、CheckboxLiteTileは
チェックボックスとその横に文字列を表示できるもので、ユーザーに質問し、Yes・Noの回答を受け取るWidgetになります


CheckboxListTile

 
Checkbox これにtitle、テキストを追加したWidgetです

 

 
基本的なコードです

bool isChecked = false;
...
CheckboxListTile(
  title: const Text('CheckboxListTile'),
  value: isChecked,
  onChanged: (bool? value) {
    setState(() {
      isChecked = value!;
    });
  },
);

左端にTitle、右端にチェックボックスになりました

 

 

Checkbox と Title の配置の設定は以下の3種類あり、

  • controlAffinity: ListTileControlAffinity.leading
    • チェックボックスが左端に固定
  • controlAffinity: ListTileControlAffinity.trailing
    • チェックボックスは右端に固定
  • controlAffinity: ListTileControlAffinity.platform
    • プラットフォーム、Android iOS のデフォルトUI規則に沿う

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

]]>
[Flutter] Checkbox チェックボックスのWidget https://akira-watson.com/flutter/checkbox-widget.html?utm_source=rss&utm_medium=rss&utm_campaign=checkbox-widget <![CDATA[nyan]]> Thu, 05 Sep 2024 02:41:59 +0000 <![CDATA[Flutter]]> https://akira-watson.com/?p=14579 <![CDATA[ユーザーからの確認を取るためにチェックを入れるCheckbox Widgetはよく使います AndroidでもiOSでも同様の機能があります Checkbox   チェックを入れてユーザーの確認を取得するWidgetです...]]> <![CDATA[

ユーザーからの確認を取るためにチェックを入れるCheckbox Widgetはよく使います
AndroidでもiOSでも同様の機能があります

Checkbox

 
チェックを入れてユーザーの確認を取得するWidgetです
 


 

シンプルに□の升にチェックを入れるための
最低限の設定はこのようになります

bool isChecked = false;
...
Checkbox(
  value: isChecked,
  onChanged: (bool? value) {
    setState(() {
      isChecked = value!;
    });
  },
);

  •  value
    • チェックされたどうかの状態
  • onChanged
    • ボックスをチェックされた時の挙動を受ける

チェックされた状態

 

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

]]>
[Flutter] SnackBar を表示させる https://akira-watson.com/flutter/scaffoldmessenger-snackbar.html?utm_source=rss&utm_medium=rss&utm_campaign=scaffoldmessenger-snackbar <![CDATA[nyan]]> Mon, 02 Sep 2024 07:06:15 +0000 <![CDATA[Flutter]]> https://akira-watson.com/?p=14571 <![CDATA[Toastのようにユーザーにメッセージを出して認識してもらうためのWidgetとしてSnackBarがあります Androidでは、そのまま SnackBar というのがありますが、iOSには同じものはありません 似たも...]]> <![CDATA[

Toastのようにユーザーにメッセージを出して認識してもらうためのWidgetとしてSnackBarがあります
Androidでは、そのまま SnackBar というのがありますが、iOSには同じものはありません
似たものとしては、メッセージを表示するToastやUIAlertControllerがあります

 

SnackBar

 
最初に、SnackBarはこちらにあるようにScaffoldが重要になります
また、メッセージの表示以外のアクションをさせることも可能です

  1. Create a Scaffold
  2. Display a SnackBar
  3. Provide an optional action

「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が確認されます

 

 

 

No ScaffoldMessenger widget found

 
SnackBar でこのようなエラーになった場合、

No ScaffoldMessenger widget found.
MainApp widgets require a ScaffoldMessenger widget ancestor.

 


 
こちらに説明がありますが
 

I am trying to create a snackbar on the click of a button in flutter, but getting exception that No ScaffoldMessenger widget found. The same code seems to work ...
 
SnackBarを表示するために必要な ScaffoldMessenger が
context の上位に存在しないとエラーになり
Scaffoldを正しく親ウィジェットとして配置する必要があるようです

ScaffoldMessenger.of(context).showSnackBar(snackBar);
この context が Scaffold の子Widgetであることを確認しないといけません
 

 

 
 

References:
SnackBar class
Display a snackbar
ScaffoldMessenger class

]]>
[Flutter] Slider を動かす https://akira-watson.com/flutter/slider.html?utm_source=rss&utm_medium=rss&utm_campaign=slider <![CDATA[nyan]]> Mon, 02 Sep 2024 02:32:14 +0000 <![CDATA[Flutter]]> https://akira-watson.com/?p=14564 <![CDATA[FlutterのSliderは、AndroidではSeekBar、iOSではUISliderとかSliderと呼ばれるもので ツマミを動かすボリュームのようなものです Slider   Sliderのツマミを持って動かす...]]> <![CDATA[

FlutterのSliderは、AndroidではSeekBar、iOSではUISliderとかSliderと呼ばれるもので
ツマミを動かすボリュームのようなものです

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

]]>
[Flutter] CircularProgressIndicator 円形プログレッシブの作成 https://akira-watson.com/flutter/circularprogressindicator.html?utm_source=rss&utm_medium=rss&utm_campaign=circularprogressindicator <![CDATA[nyan]]> Sat, 31 Aug 2024 12:16:59 +0000 <![CDATA[Flutter]]> https://akira-watson.com/?p=14557 <![CDATA[LinearProgressIndicatorではプログレッシブバーで進捗状況を表示できましたが 円弧で進捗を示せるのが、CircularProgressIndicator です CircularProgressIndi...]]> <![CDATA[

LinearProgressIndicatorではプログレッシブバーで進捗状況を表示できましたが
円弧で進捗を示せるのが、CircularProgressIndicator です

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 でプログレッシブバーの作成 https://akira-watson.com/flutter/linear-progress-indicator.html?utm_source=rss&utm_medium=rss&utm_campaign=linear-progress-indicator <![CDATA[nyan]]> Sat, 31 Aug 2024 08:18:18 +0000 <![CDATA[Flutter]]> https://akira-watson.com/?p=14549 <![CDATA[状態の進捗状況を示すために、プログレッシブバーが使われることがあります FlutterではLinearProgressIndicatorを利用するとリニアな変化で進捗状況を表示できます LinearProgressInd...]]> <![CDATA[

状態の進捗状況を示すために、プログレッシブバーが使われることがあります
FlutterではLinearProgressIndicatorを利用するとリニアな変化で進捗状況を表示できます

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

]]>