はじめに
とあるスマホアプリ開発のために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 |
子ウィジェット間および端の余白を均等に分ける。 |
コード例
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を利用して全体の要素を寄せたということです。
コード例
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に書き換えたらいいだけ
画像例
コード例
ほぼ同じだが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
例えば画面からウィジェットの一部分がはみ出したとき
その一部分をスクロールして表示できるようにする
コード例
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
先ほどまでのものでディフォルトで子ウィジェットがはみ出ててもスクロールできるウィジェット。
しかしリソース的にはすべて解釈しているので結構食べるみたい
コード例
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)),
),
),
],
),
);
}
}
画面遷移について
この記事が体系的ですごくわかりやすいので見てみてください
プッシュ遷移について
今回の作成画像と遷移例
遷移前 | 遷移後 |
---|---|
基本構文
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
と書けます。
コード例
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
モーダル遷移
iOSとAndroidでは遷移の方向が違うので注意です。
ベゼルスワイプ(左端から一個ずつ戻っていく機能)はiOS限定(プッシュ遷移)
画面間での値の受け渡し
遷移後に値を保持しないと割と大変です。
そこで遷移後に値を保持する方法を紹介します。
例(プッシュ遷移)
ボタン押下前 | ボタン押下後 |
---|---|
例(モーダル遷移)
ボタン押下前 | ボタン押下後 |
---|---|
今回の例のように変数「id」の値を遷移によって変更する。
考え方
基本的には関数の操作における引数の受け渡しで値を管理する。
例えば「○○が押されると変数に値1が入る」このイメージである。
とはいえイメージがわきにくいので早速コードを見ていこう
※ 今回から見にくいのでファイルを分割しています
コード例
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
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
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
重要な部分だけ取り出して解説します。
(モーダル遷移も同じなのでまとめて解説します)
// プッシュ遷移のボタン
ElevatedButton(
onPressed: () {
pushfunc(
context,
id: 1,
name: "プッシュ遷移で遷移した画面",
);
},
child: const Text("プッシュ遷移 (通常遷移)"),
),
ここではプッシュ遷移のボタンが押された際の動きを定義しています。
ElevatedButtonが押されたら
onPressedで遷移関数に値を渡してます。
遷移関数の内訳がこうなります
// プッシュ遷移関数
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で管理する
// 遷移関数
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の内容を渡すだけです。
ElevatedButton(
onPressed: () {
Split(
context,
id: 1,
name: "プッシュ遷移で遷移した画面",
flag: false // 変更点
);
},
child: const Text("プッシュ遷移 (通常遷移)"),
),
以上のような形で値を受け渡し等できます。
参考資料等
ChatGPT
Udemy Flutterアプリ開発講座(初級編)
Fluteerで始めるアプリ開発
備忘録その1
Dart基礎文法