Skip to content

DNET_FlutterFirstStep

nishi_74322014 edited this page Sep 11, 2026 · 1 revision

Flutterのファースト・ステップ

概要

Flutter の step by step(其の一)。

手順1:インストールから実行まで。

コチラ(の該当節)を参考にして実施

インストール

ダウンロード

https://flutter.dev/docs/get-started/install/windows

解凍&パス指定

flutter_windows_x.x.x-stable.zip

  • 解凍

    C:\prog\dev\flutter
    
  • パス指定

    // ユーザーの環境変数に設定する例
    $path =[System.Environment]::GetEnvironmentVariable('PATH','User')
    $path +=";" + "C:\prog\dev\flutter\bin";
    [System.Environment]::SetEnvironmentVariable('PATH',$path,'User')

チェック(1)

以下のCommandを実行し、TODOを確認する。

>flutter doctor

Android Studioのインストール

  • Android Studioのインストール(Android Studioの該当節を参照)

  • 既定値でインストール(Android Virtual Device(AVD)もインストール)。

  • 最後に、ライセンス条項に同意する。

    flutter doctor --android-licenses
    
  • 初期設定(Android Studioの該当節を参照)

    • 必要に応じて、Android Targetをインストール
    • 日本語化したら問題が出るらしい(現時点で解決しているか不明)。
  • PATH環境変数(Android Studioの該当節を参照)

    • ツール類

      %USERPROFILE%\AppData\Local\Android\Sdk\tools
      %USERPROFILE%\AppData\Local\Android\Sdk\tools\bin
      %USERPROFILE%\AppData\Local\Android\Sdk\platform-tools
      
    • gradleのbin

      %USERPROFILE%\.gradle\wrapper\dists\gradle-6.7-all\cuy9mc7upwgwgeb72wkcrupxe\gradle-6.7\bin
      

    ※ 前述のPowerShellだと、%USERPROFILE%が展開されねぇんだけど...。

プラグインのインストール

「File」メニュー -> 「Settings..」 -> 「Plugins」

  • Flutterプラグインの「Install」ボタンを押下
  • Dartプラグインの「Install」ボタンを押下

チェック(2)

以下のCommandを実行し、問題が無いことを確認する。

>flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 2.0.3, on Microsoft Windows [Version
10.0.19041.867], locale ja-JP)
[√] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
[√] Chrome - develop for the web
[√] Android Studio (version 4.1.0)
[√] VS Code, 64-bit edition (version 1.47.2)
[√] Connected device (2 available)

• No issues found!

>

余談:SDKのパス変更

SDKのパスを変更する場合、以下の手順に従って変更する。

サンプルの生成と実行

新規作成プロジェクト

Android Studioで、新規作成プロジェクトする。

  • 「Start Flutter New Project」
    or 「File -> New -> New Flutter Project..」

  • 「Flutter Application」を選択

プロジェクトタイプ 概要
Flutter Application アプリケーション用
Flutter Plugin Android ネイティブAPI用ライブラリ用
Flutter Package 画面部品用
Flutter Module Dartライブラリ用
  • 以下を入力する。
入力項目 概要
Project Name プロジェクト名
Flutter SDK path Project location Flutter SDKをインストールしたフォルダを指定。
Description プロジェクト、アプリの説明
Package Name 通常、ドメインを反対にした値を入力
  • コチラ(の該当節)のコードが生成される。

デバッグ実行(サンプル)

  • 実行するデバイスでChromeなどのブラウザを選択し、
  • 緑三角(実行)、虫(デバッグ)ボタンを押すと実行。
  • ブラウザではなく、エミュレータや実機を使う手順はコチラ

プロジェクトの開発

以降の手順で説明する。

デバッグ実行

Android Studioで1GB、エミュレータで6GB程、メモリを消費するので、
開発機のメモリが16GB程度では少々、スペック不足、故に実機でのデバッグを行うことになる。

エミュレータを登録して実行

  • 必要に応じて、Windows ハイパーバイザー プラットフォーム(Androidのエミュレータの該当節を参照)を有効化しておく。
    (コレにより、Hyper-V、Docker for Windows、WSL2との同居が可能に)

  • コチラ(の「AVDエミュレータ作成」)の新し目の手順になる。

    • エミュレータの登録

      • 「Tools」メニュー -> 「AVD Manager」
      • (「Create Virtual Device ..」でデバイスを選択し「Next」ボタンを押下)
      • (API Levelを選択(最新に近いモノを選択する))
      • 「Actions」から三角ボタンを押して、エミュレータを起動
    • エミュレータでデバッグ実行

      • 実行するデバイスを選択し、
      • 緑三角(実行)、虫(デバッグ)ボタンを押すと実行。

実機でデバッグ実行する場合

  • IDEサポート(Flutter - ビルドとデプロイの該当節を参照)
    最近の開発環境では、IDEサポートが追加されている。

  • マニュアル

    • 「Build」メニュー -> 「Flutter」 -> 「Build APK」
      を実行して、apkファイルをプロジェクト出力。

    • コチラ(の該当節)の手順を参考にして、以下のフォルダのapkファイルを送り込む。

      %HOMEPATH%\AndroidStudioProjects\(flutter_app)\build\app\outputs\flutter-apk\app-release.apk
      

      ※ コマンドの実行に、PATH環境変数の設定は必須。

Flutter Devtoolsを利用する。

  • 「Tools」メニュー -> 「Flutter」 -> 「Open Flutter Devtools」を実行して、
  • デバッグ実行中に、[Flutter Inspector]のペインを開くとUI要素のツリーが表示される。

FlutterInspector

  • Flutter Devtoolsでは、その他、様々なツールを使用できる。
    • Timeline
    • Memory
    • Performance
    • Debugger
    • Logging

その他のツールを利用する。

手順2:イベントを実装する。

ボタンのWidget

  • 以下のようなボタンがある。
  • 「() {}」(空のラムダ式)を、
    生成されたコード中にある、
    「_incrementCounter」に変更してテスト可能。

RaisedButton

  • onPressedに、任意のメソッドを書く。
  • deprecatedらしい→ElevatedButton
RaisedButton(
  child: const Text('Button'),
  color: Colors.orange,
  textColor: Colors.white,
  onPressed: () {},
),

ElevatedButton

onPressedに、任意のメソッドを書く。

ElevatedButton(
  child: const Text('Button'),
  style: ElevatedButton.styleFrom(
    primary: Colors.orange,
    onPrimary: Colors.white,
  ),
  onPressed: () {},
),

FlatButton

  • onPressedに、任意のメソッドを書く。
  • deprecatedらしい→TextButton
FlatButton(
  child: const Text('Button'),
  textColor: Colors.black,
  onPressed: () {},
),

TextButton

onPressedに、任意のメソッドを書く。

TextButton(
  child: const Text('Button'),
  style: TextButton.styleFrom(
    primary: Colors.black,
  ),
  onPressed: () {},
),

OutlineButton

  • onPressedに、任意のメソッドを書く。
  • deprecatedらしい→OutlinedButton
OutlineButton(
  child: const Text('Button'),
  onPressed: () {},
),

OutlinedButton

onPressedに、任意のメソッドを書く。

OutlinedButton(
  child: const Text('Button'),
  style: OutlinedButton.styleFrom(
    primary: Colors.black,
  ),
  onPressed: () {},
),

IconButton

未記載(元 Wiki も見出しのみ)。

ButtonBar

未記載(元 Wiki も見出しのみ)。

FloatingActionButton

未記載(元 Wiki も見出しのみ)。

PopupMenuButton

未記載(元 Wiki も見出しのみ)。

DropdownButton

任意のWidget

ボタンでない、任意のWidgetに、イベントを実装できる。

InkWell

簡単に、Ripple エフェクトを付ける事が出来る。

Material(                  // 親にMaterialが必須
 color: Colors.blue,       // Material自体は青を指定
 child: Center(            //
   child: Ink(             // ここをContainerにするとsplashが効かないので、Inkに変える
     color: Colors.yellow, // 背景色はここで指定
     width: 200.0,
     height: 100.0,
     child: InkWell(
         onTap: () {},
         child: Center(child: Text('YELLOW'))
     ),
   ),
 ),
)

GestureDetector

InkWellのRipple エフェクトは無いが、
他のイベント(ジェスチャー)にも反応する。

GestureDetector(
  // タッチ検出対象のWidget
  child: Text(
    'How to use GestureDetector',
    textAlign: TextAlign.center,
    overflow: TextOverflow.ellipsis,
    style: TextStyle(fontWeight: FontWeight.bold),
  ),
  // ダブルタップ
  onDoubleTap: _incrementCounter
)

参考(手順2)

Qiita(手順2)

手順3:画面遷移処理を実装する。

  • 画面遷移の実装について。
  • MaterialApp以下で実装。
    • MaterialApp が持っている Navigator を使って画面遷移。
    • Navigatorの動作イメージはStackで、一番上の Route が現在の画面。
      • 無限にPushできてしまうので、実装を考える必要がある。
      • 常にPoP & Pushだと、戻った時に、常に状態は保存されていない。
      • ...

API

push

  • Widget を MaterialPageRoute に渡して Route を作る。

  • Navigator.pushにRouteを渡して画面遷移。

Navigator.of(context).pop();
Navigator.of(context).push(
  MaterialPageRoute(
    builder: (context) {
      return MyHomePage(title: 'Flutter Demo Home PageA');
    },
  ),
);

pushNamed

  • 予め MaterialApp に Routes を登録しておく。

    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
         ...
        ),
        routes: {
          '/': (context) => MyHomePage(title: 'Flutter Demo Home Page'),
          '/message': (context) => MessageView(),
  • Navigator.pushNamedにルート名を渡して画面遷移。

    Navigator.of(context).pushNamed("/message");
    Navigator.pushNamed(context, '/message', arguments: ...);

メニュー

AppBar

Drawer

TabBar

  • 以下は、TabPageが遷移先で、Navigatorは使用しない。

  • _MyHomePageState.buildメソッドが
    Scaffoldではなく、DefaultTabControllerを返すのもポイント。

  • 参考

BottomNavigationBar

参考(手順3)

Qiita(手順3)

手順4:レイアウトを行う。

body, children or childと、レイアウトを行う。

Layout系

Container

  • どちらも原点は”左上”

  • デフォルトで

    • Columnは縦方向いっぱい
    • Rowは横方向いっぱい

    のサイズになる。

body: Container(
  ...
  child: Column or Row

Center

  • 中央に寄るのはColmn(Row)自体。
  • 内部コンテンツの位置は変わらない。
body: Center(
  ...
  child: Column or Row

その他(Layout系)

  • Align
  • Padding
  • FittedBox / BoxFit
  • SafeArea

Multi Layout系

Colmn

  • 列内の行は縦にスタック
  • 行内の内部コンテンツは横にスタック
body: Column(
  ...
  children: <Widget>[
    Row(

Row

  • 行内の列は横にスタック
  • 列内の内部コンテンツは縦にスタック
body: Row(
  ...
  children: <Widget>[
    Column(

その他(Multi Layout系)

  • Expanded
  • Stack
  • ListView / ListTile
  • TabBar / Tab / TabBarView
  • BottomNavigationBar

margin/padding

margin、paddingプロパティ

  • childプロパティのあるWidgetの、
    margin、paddingプロパティにEdgeInsetsを指定する。

    margin: EdgeInsets.only(top: 100),
    padding: EdgeInsets.only(top: 50),
  • EdgeInsetsには以下のメソッドがある。

    • only:上下左右のmargin/paddingを指定
    • fromLTRB:onlyの名前無し引数版(上下左右)
    • symmetric:縦方向横方向のmargin/paddingを指定
    • all:全方向のmargin/paddingを指定
  • 参考

margin、paddingプロパティが無い場合

RowやColumnなど、childrenプロパティのあるWidgetは、
margin、paddingプロパティを持たないが、
Containerをネストして使用すると面倒なので、
SizedBoxを拡張したSpaceBoxを使用すると良い。

  • 参考
    • Padding(またはMargin)だけが目的でContainerを
      使うのは間違ってる(というか分かりづらい) -Flutter-

AxisAlignment

内部コンテンツの位置

MainAxisAlignment

  • 前述の、...の部分に、

    mainAxisAlignment: MainAxisAlignment.XXXXX,

    ...と書き、主軸方向の配置位置を調整する。

    • start/end
      先頭/末尾寄せ
    • center
      中央寄せ
    • spaceEvenly
      間隔を均等に配置
    • spaceAround
      各余白を均等に配置
    • spaceBetween
      最大限に分散配置

CrossAxisAlignment

  • 前述の、...の部分に、

    crossAxisAlignment: CrossAxisAlignment.XXXXX,

    ...と書き、交差軸方向の配置位置を調整する。

    • start/end
      先頭/末尾寄せ
    • center
      中央寄せ
    • stretch
      引き伸ばして配置
    • baseline
      ベースラインを揃えて配置
  • SizedBox.expand
    交差軸方向のスペースをすべて取らない場合、
    CrossAxisAlignmentが適切に機能しない事がある。
    この場合は、SizedBox.expandで、スペースを消費させる。

    body: SizedBox.expand(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          ...,
        ],
      ),
    ),

参考(手順4)

手順5:パッケージの追加と利用

english_wordsパッケージの例

パッケージの追加(english_words)

english_wordsパッケージを追加・利用する。

パッケージの利用(english_words)

  • インポートした上で

    import 'package:english_words/english_words.dart';
  • ライブラリを利用する(コチラのコードを修正)。

    class _MyHomePageState extends State<MyHomePage> {
      String _counter = 'hoge';
    
      void _incrementCounter() {
        setState(() {
          // This call to setState tells the Flutter framework that something has
          // changed in this State, which causes it to rerun the build method below
          // so that the display can reflect the updated values. If we changed
          // _counter without calling setState(), then the build method would not be
          // called again, and so nothing would appear to happen.
          _counter = WordPair.random().asPascalCase;
        });
      }

データの永続化(shared_preferences)

パッケージの追加(shared_preferences)

shared_preferencesパッケージを追加する必要がある。

パッケージの利用(shared_preferences)

import 'package:shared_preferences/shared_preferences.dart';

Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
late Future<int> _counter;

Future<void> _incrementCounter() async {
  final SharedPreferences prefs = await _prefs;
  final int counter = (prefs.getInt('counter') ?? 0) + 1;
  setState(() {
    _counter = prefs.setInt("counter", counter).then((bool success) {
      return counter;
    });
  });
}

@override
void initState() {
  super.initState();
  _counter = _prefs.then((SharedPreferences prefs) {
    return (prefs.getInt('counter') ?? 0);
  });
}

参考(shared_preferences)

ブラウザ起動(url_launcher)

パッケージ追加(url_launcher)

url_launcherパッケージを追加する必要がある。

パッケージの利用(url_launcher)

import 'package:url_launcher/url_launcher.dart';

_launchURL() async {
  const url = "http://https://www.google.co.jp/";
  if (await canLaunch(url)) {
    await launch(url);
  } else {
    throw 'Could not Launch $url';
  }
}

参考(url_launcher)

WebAPIを呼び出す。

パッケージ追加(http)

HTTPパッケージを追加する必要がある。

GET

  • Example
    https://pub.dev/packages/http/example

    import 'dart:convert' as convert;
    import 'package:http/http.dart' as http;
    
    // This example uses the Google Books API to search for books about http.
    // https://developers.google.com/books/docs/overview
    var url =
        Uri.https('www.googleapis.com', '/books/v1/volumes', {'q': '{http}'});
    
    // Await the http get response, then decode the json-formatted response.
    var response = await http.get(url);
    if (response.statusCode == 200) {
      var jsonResponse =
          convert.jsonDecode(response.body) as Map<String, dynamic>;
      var itemCount = jsonResponse['totalItems'];
      print('Number of books about http: $itemCount.');
    } else {
      print('Request failed with status: ${response.statusCode}.');
    }
  • 作法

    • Futureのメソッドチェーン

      void sendRequest() {
        http
          .get(url)
          .then((response) {
            print(response);
          })
          .catchError((error) => print(error));
      }
    • async/await

      Future<void> sendRequest() async {
        try {
          final response = await http.get(url);
          print(response);
        } on Exception catch (error) {
          print(error);
        }
      }

POST

  • 作法

    • Futureのメソッドチェーン

      ...
      
    • async/await

      var response = await http.post(
          "https://www.example.com/path/to/api",
          body: {
              "nickname": "chooyan",
              "description": "A freelance mobile app developer",
          },
      );
  • コンテンツ

    • application/x-www-form-urlencoded
      bodyに、Map<String, String>型 の変数を指定する。

    • application/json

      • JSON形式のString型を指定する。
      • json.encode(Map<String, String>型)でもOK。

その他(WebAPI)

  • ヘッダ
    headersに、Map<String, String>型 の変数を指定する。

  • ...

参考(WebAPI)

参考(手順5)

参考

Flutterの参考

  • Flutterの参考(Flutterの該当節を参照)

Flutterのセカンド・ステップ

正誤

  • sendRequest() の async/await 版の on Exception caach (error)catch の誤記のため修正した。

移行メモ

  • 元 Wiki では見出しそのものが他ページ・同ページ内へのリンクになっていた箇所 (「Android Studioのインストール」「Flutterの参考」「Flutterのセカンド・ステップ」)は、 GitHub Wiki では見出しからアンカが生成されるため、 見出しをプレーン・テキストとし、リンクは直下の本文に置いた。
  • 同名の見出し(「チェック(2)」の元アンカが「チェック(1)」と同一 ID だった件を含め、「デバッグ実行」「参考」「Qiita」「パッケージの追加」 「パッケージの利用」「その他」)が多数あり GitHub Wiki でアンカが衝突するため、 括弧で文脈を補って一意にした。
  • Map<String, String> などの山括弧は、そのままでは HTML タグとして 解釈される箇所があるため、本文中では実体参照(&lt; / &gt;)で記述した。
  • PukiWiki のページ内アンカ(#xxxxxxxx)は GitHub Wiki では再現できないため、 同一ページ内のアンカは見出しから生成されるアンカに張り替え、 他ページのアンカを指すリンクは「〜(ページ名 の該当節を参照)」の形に置き換えた。
  • マイクロソフト系技術情報 Wiki(techinfoofmicrosofttech.osscons.jp)への URL リンクは張り替え済み。
  • 元 Wiki の行頭空白によるプレフォーマット・ブロックは、 フェンス付きコードブロックにした。
  • 本文の無い見出しは、その旨を明示した。
  • リンク先が未移行のページは、移行先のファイル名を `DNET_○○.md` の形で示し TODO.md に登録した。

Tags: 移行, Flutter, Dart, Android Studio, flutter doctor, Widget, Navigator, レイアウト, パッケージ, DevTools

NetDevInfraWiki

マイクロソフト系技術情報 Wiki
Open 棟梁 Wiki

(未着手)

開発基盤部会 Wiki

移行管理: DONETODO

Clone this wiki locally