0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Qiita全国学生対抗戦Advent Calendar 2024

Day 5

flutter備忘録その2

Last updated at Posted at 2024-12-16

はじめに

とあるスマホアプリ開発のためにFlutterが必要となったのでそれの備忘録その2です。
色々なサイトなど参考にさせていただきまして、もしかしたらその内容に欠けてることもありますが、少しでも参考になればいいなと思います。


Scaffoldについて

よく、ウィジェットと追加するさいにScaffoldの中に記述することが多い。
これはアプリの骨組みのようなイメージのウィジェットになる。

Column Rowについて

Column

ウィジェットを縦方向に1列に並べるウィジェット
childrenという要素を持ち、それの要素はリスト構造になっている
書き方イメージ

Column(
    children: [
    image1...
    image2...
    image3...
    ]
)

として子ウィジェットの中にあるリストを縦方向に表示する。

並べる順番の優先を決める要素
mainAxisAlignmentこの場合上から下を指すようになる

設定できるもの

説明
MainAxisAlignment.start 子ウィジェットを上側(開始位置)に配置する。
MainAxisAlignment.end 子ウィジェットを下側(終了位置)に配置する。
MainAxisAlignment.center 子ウィジェットを中央に配置する。
MainAxisAlignment.spaceBetween 子ウィジェット間のスペースを均等に分け、端に余白は作りません。
MainAxisAlignment.spaceAround 子ウィジェット間に均等なスペースを空け、端にも同じ大きさの余白を作る。
MainAxisAlignment.spaceEvenly 子ウィジェット間および端の余白を均等に分ける。

使用例画像(今回はcenterを利用)

コード例
main.dart
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Column Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Columnで遊ぼう'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Container(
            width: 300,
            height: 300,
            color: Colors.red,
            child: const Center(
                child: Text('赤', style: TextStyle(color: Colors.white))),
          ),
          Container(
            width: 200,
            height: 200,
            color: Colors.green,
            child: const Center(
                child: Text('緑', style: TextStyle(color: Colors.white))),
          ),
          Container(
            width: 100,
            height: 100,
            color: Colors.blue,
            child: const Center(
                child: Text('青', style: TextStyle(color: Colors.white))),
          ),
        ],
      ),
    );
  }
}

crossAxisAligment

子要素の位置を調整できる
画像例

地味にだが子要素が右から始まっており
右したに全体のコンテナーがよっていることがわかる
これはAlignを利用して全体の要素を寄せたということです。

コード例
main.dart
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Column Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Columnで遊ぼう'),
      ),
      body: Align(
        alignment: Alignment.bottomRight, // 右下に配置
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.end,
          mainAxisSize: MainAxisSize.min, // Columnのサイズを子ウィジェットに合わせる
          children: [
            Container(
              width: 300,
              height: 300,
              color: Colors.red,
              child: const Center(
                  child: Text('赤', style: TextStyle(color: Colors.white))),
            ),
            Container(
              width: 200,
              height: 200,
              color: Colors.green,
              child: const Center(
                  child: Text('緑', style: TextStyle(color: Colors.white))),
            ),
            Container(
              width: 100,
              height: 100,
              color: Colors.blue,
              child: const Center(
                  child: Text('青', style: TextStyle(color: Colors.white))),
            ),
          ],
        ),
      ),
    );
  }
}

Row(横方向に並べてみる)

これまでColumnで縦方向に子要素を並べるように設定していたが横に並べてみる

つまりColumnをRowに書き換えたらいいだけ
画像例

コード例 ほぼ同じだが
main.dart
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Column Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Rowで遊ぼう'),
      ),
      body: Align(
        alignment: Alignment.center, // 右下に配置
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          mainAxisSize: MainAxisSize.min, // Rowのサイズを子ウィジェットに合わせる
          children: [
            Container(
              width: 200,
              height: 200,
              color: Colors.red,
              child: const Center(
                  child: Text('赤', style: TextStyle(color: Colors.white))),
            ),
            Container(
              width: 100,
              height: 100,
              color: Colors.green,
              child: const Center(
                  child: Text('緑', style: TextStyle(color: Colors.white))),
            ),
            Container(
              width: 50,
              height: 50,
              color: Colors.blue,
              child: const Center(
                  child: Text('青', style: TextStyle(color: Colors.white))),
            ),
          ],
        ),
      ),
    );
  }
}

SingleChildScrolView

例えば画面からウィジェットの一部分がはみ出したとき
その一部分をスクロールして表示できるようにする

コード例
main.dart
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Column Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Rowで遊ぼう'),
      ),
      body: SingleChildScrollView(
        scrollDirection: Axis.horizontal, // 横方向にスクロール可能
        child: Align(
          alignment: Alignment.center, // コンテンツを中央に配置
          child: Row(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisSize: MainAxisSize.min, // Rowのサイズを子ウィジェットに合わせる
            children: [
              Container(
                width: 400,
                height: 400,
                color: Colors.red,
                child: const Center(
                    child: Text('赤', style: TextStyle(color: Colors.white))),
              ),
              Container(
                width: 300,
                height: 300,
                color: Colors.green,
                child: const Center(
                    child: Text('緑', style: TextStyle(color: Colors.white))),
              ),
              Container(
                width: 200,
                height: 200,
                color: Colors.blue,
                child: const Center(
                    child: Text('青', style: TextStyle(color: Colors.white))),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Listview

先ほどまでのものでディフォルトで子ウィジェットがはみ出ててもスクロールできるウィジェット。

しかしリソース的にはすべて解釈しているので結構食べるみたい

コード例
main.dart
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ListView Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('ListViewで遊ぼう'),
      ),
      body: ListView(
        children: [
          Container(
            width: double.infinity, // 幅を画面全体に広げる
            height: 400,
            color: Colors.red,
            child: const Center(
              child: Text('赤', style: TextStyle(color: Colors.white)),
            ),
          ),
          Container(
            width: double.infinity, // 幅を画面全体に広げる
            height: 300,
            color: Colors.green,
            child: const Center(
              child: Text('緑', style: TextStyle(color: Colors.white)),
            ),
          ),
          Container(
            width: double.infinity, // 幅を画面全体に広げる
            height: 200,
            color: Colors.blue,
            child: const Center(
              child: Text('青', style: TextStyle(color: Colors.white)),
            ),
          ),
          Container(
            width: double.infinity, // 幅を画面全体に広げる
            height: 200,
            color: Colors.yellow,
            child: const Center(
              child: Text('青', style: TextStyle(color: Colors.white)),
            ),
          ),
        ],
      ),
    );
  }
}

画面遷移について

この記事が体系的ですごくわかりやすいので見てみてください

プッシュ遷移について

  • 新しい画面を現在の画面にプッシュすること
  • 元の画面は下にある状態
  • 詳細とかもろもろ出すときに便利
  • プッシュ画面の例:メモ帳(著作者がiPhoneのためiOSです...)

今回の作成画像と遷移例

遷移前 遷移後

基本構文

Navigator.push(
    
    context,     // 現在のページの状態
    
    MaterialPageRoute(
     builder:(context){ // 現在の情報を引数として渡す
     
        return // プッシュ遷移で遷移したいページ情報;
    
    }, // builder
    fullscreenDialog: false, // trueでモーダル遷移
   ), // MaterialPageRoute
 );  // Navigator.push

以上のような典型的な書き方があります。

この書き方を実践的な利用例に置き換えると

下記のサンプルコードを引用して

// ボタンが押されたら
onPressed: () { 
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context){
                    return const NextPage(); // ページ呼び出し
                }, // 次のページ呼び出し
                fullscreenDialog: false, // trueでモーダル遷移
              ), // MaterialPageRoute
            ); // Navigator.push

と書けます。

コード例
main.dart
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ListView Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
        useMaterial3: true,
      ), // ThemeData
      home: const MyHomePage(),
    ); // MaterialApp
  } // build
} // MyApp

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('プッシュ遷移で遊ぼう'), // appbar
      ), // AppBar
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.push(
              // 以下ボタン押されたときの処理
              context,
              MaterialPageRoute(
                builder: (context){
                 return const NextPage();
                }, // 次のページ呼び出し
                fullscreenDialog: false, // trueでモーダル遷移
              ), // MaterialPageRoute
            ); // Navigator.push
          },
          child: const Text("次のページへ"), // ボタン内に表示される内容
        ), // ElevatedButton
      ), // Center
    ); // Scaffold
  } // build
} // MyHomePage

// 遷移後のページのクラスを定義
class NextPage extends StatelessWidget {
  const NextPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // 中身に由来するScaffold
      appBar: AppBar(
        title: const Text("NextPage"),
      ), // AppBar
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              "NextPageです",
              style: TextStyle(fontSize: 16),
            ), // Text
            ElevatedButton(
              onPressed: () {
                Navigator.pop(context);
              },
              child: const Text("戻る"),
            ), // ElevatedButton
          ], // children
        ), // Column
      ), // Center
    ); // Scaffold
  } // build
} // NextPage

モーダル遷移

  • 新しい画面が現在の上に表示されるもの
  • モダール画面の例:メモ帳(著作者がiPhoneのためiOSです...)

iOSとAndroidでは遷移の方向が違うので注意です。
ベゼルスワイプ(左端から一個ずつ戻っていく機能)はiOS限定(プッシュ遷移)

画面間での値の受け渡し

遷移後に値を保持しないと割と大変です。
そこで遷移後に値を保持する方法を紹介します。

例(プッシュ遷移)

ボタン押下前 ボタン押下後

例(モーダル遷移)

ボタン押下前 ボタン押下後

今回の例のように変数「id」の値を遷移によって変更する。

考え方
基本的には関数の操作における引数の受け渡しで値を管理する。
例えば「○○が押されると変数に値1が入る」このイメージである。

とはいえイメージがわきにくいので早速コードを見ていこう
※ 今回から見にくいのでファイルを分割しています

コード例
main.dart
import 'package:flutter/material.dart';
import 'TopPage.dart';

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

// エントリーポイントクラス
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Example',
      home: MyHomePage(),
    ); // MaterialApp
  } // build
} // MyApp
TopPage.dart
import 'package:flutter/material.dart';
import 'NextPage.dart';

// Topページクラス
class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  // プッシュ遷移関数
  void pushfunc(BuildContext context, {required id, required name}) {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) {
          return NextPage(id: id, name: name); // 遷移先のページを指定
        },
        fullscreenDialog: false, // プッシュ遷移
      ), // MaterialPageRoute
    ); // Navigator.push
  } // pushfunc

  // モーダル遷移関数
  void modalfunc(BuildContext context, {required id, required String name}) {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) {
          return NextPage(id: id, name: name); // 遷移先のページを指定
        },
        fullscreenDialog: true, // モーダル遷移
      ), // MaterialPageRoute
    ); // Navigator.push
  } // modal

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('プッシュ & モーダル遷移で遊ぼう'), // Text
      ), // AppBar

      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // プッシュ遷移のボタン
            ElevatedButton(
              onPressed: () {
                pushfunc(
                  context,
                  id: 1,
                  name: "プッシュ遷移で遷移した画面",
                );
              },
              child: const Text("プッシュ遷移 (通常遷移)"),
            ),
            const SizedBox(height: 16), // ボタン間のスペース

            // モーダル遷移のボタン
            ElevatedButton(
              onPressed: () {
                modalfunc(
                  context,
                  id: 2,
                  name: "モーダル遷移で遷移した画面",
                );
              },
              child: const Text("モーダル遷移"),
            ),
          ],
        ),
      ), // Center
    ); // Scaffold
  } // build
} // MyHomePage

NextPage.dart
import 'package:flutter/material.dart';

// 遷移後のページ
class NextPage extends StatelessWidget {
  // コンストラクタ
  const NextPage({required this.id, required this.name});

  // メンバ変数
  final int id;
  final String name;

  // オーバーライド
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // 中身に由来するScaffold
      appBar: AppBar(
        title: const Text("NextPage"),
      ), // AppBar

      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              "NextPageです\n id = $id name = $name",
              style: TextStyle(fontSize: 16),
            ), // Text
            ElevatedButton(
              onPressed: () {
                Navigator.pop(context);
              },
              child: const Text("戻る"),
            ), // ElevatedButton
          ], // children
        ), // Column
      ), // Center
    ); // Scaffold
  } // build
} // NextPage

重要な部分だけ取り出して解説します。
(モーダル遷移も同じなのでまとめて解説します)

TopPage.dart
// プッシュ遷移のボタン
ElevatedButton(
  onPressed: () {
    pushfunc(
      context,
      id: 1,
      name: "プッシュ遷移で遷移した画面",
    );
  },
  child: const Text("プッシュ遷移 (通常遷移)"),
),

ここではプッシュ遷移のボタンが押された際の動きを定義しています。
ElevatedButtonが押されたら

onPressedで遷移関数に値を渡してます。
遷移関数の内訳がこうなります

TopPage.dart
// プッシュ遷移関数
  void pushfunc(BuildContext context, {required id, required name}) {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) {
          return NextPage(id: id, name: name); // 遷移先のページを指定
        },
        fullscreenDialog: false, // プッシュ遷移
      ), // MaterialPageRoute
    ); // Navigator.push
  } // pushfunc

引数を解説すると

項目 説明
context 現在の画面の情報を格納(必須項目)
id 今回変動させる値。名前付き引数で指定している。
name テキストの内容を入れる変数。名前付き引数で指定している。

このようになってます。

ここで重要なのが fullscreenDialog: false, //
この部分が「false」になっているのでプッシュ遷移となる
ちなみに「true」だとモーダル遷移となる。
その後はコード例でも示している、「NextPage.dart」のクラスが実行される


今回、わかりやすさ優先で遷移ごとに関数を分けたがbool型で管理する事もできる
その場合、遷移関数が一つで済む。

bool型の利用例

① 遷移関数の引数にbool型を定義する
fullscreenDialog:をflagで管理する

TopPage.dart
 // 遷移関数
  void Split(BuildContext context,
      {required id, required name, required bool flag}) {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) {
          return NextPage(id: id, name: name); // 遷移先のページを指定
        },
        fullscreenDialog: flag, // プッシュ遷移
      ), // MaterialPageRoute
    ); // Navigator.push
  } // pushfunc

あとは引数でflagの内容を渡すだけです。

TopPage.dart
ElevatedButton(
  onPressed: () {
    Split(
      context, 
      id: 1, 
      name: "プッシュ遷移で遷移した画面", 
      flag: false // 変更点
      );
  },
  child: const Text("プッシュ遷移 (通常遷移)"),
),

以上のような形で値を受け渡し等できます。


参考資料等

ChatGPT

Udemy Flutterアプリ開発講座(初級編)

Fluteerで始めるアプリ開発

備忘録その1

Dart基礎文法

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?