アイコン一覧
https://qiita.com/hosikiti/items/6e460ad325ce59f026d0
画像の登録
ディレクトリ作成し、そこにドラッグ&ドロップ
pubspec.yamlのassetsのコメントを外し、追加した画像ファイルの相対パスを記載する
main.dart
// インターネット上の画像を表示するウィジェット
Image.network(
'https://jumpneeyan.com/wp-content/uploads/2020/10/kurapika_img_1.png',
width: 300,
// アスペクト比を保持したまま小さいのサイズを優先
height: 100,
),
// プロジェクト内の画像を表示するウィジェット
Image.asset(
'assets/kurapika.jpg',
width: 300,
height: 500,
)
ウィジェット実装サンプル
main.dart
// マテリアルデザインをFlutterで実装するための部品群
import 'package:flutter/material.dart';
// 一番最初に実行されるメソッド
void main() {
// 引数はアプリの大元のウィジェット
runApp(const MyApp());
}
// StatelessWidgetは状態を持たないウィジェット
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
// マテリアルデザインアプリを構築するための大元のウィジェット
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
// StatefulWidget状態をもつウィジェット
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
// _MyHomePageStateにMyHomePageの状態を継承させる
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
// setStateはUIに通知するメソッド
setState(() {
print('Before $_counter');
_counter++;
print('After $_counter');
});
}
@override
Widget build(BuildContext context) {
// Scaffoldはアプリの最も基本的な土台。appBarやbodyというパートを持つ
// floatingActionButton要素もある(次へボタンに相当)
return Scaffold(
// 背景色。ここで条件分を記載できる
backgroundColor: _counter % 4 == 0 ? Colors.yellow : Colors.blue,
// AppBarはナビバーの部分
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
// widget.titleには上位で指定された'Flutter Demo Home Page'が入っている
title: Text(widget.title),
),
// bodyは本文
body:
// これでスクロール可能になる(セルの再利用するにはListViewを使う)
SingleChildScrollView(
// scrollDirectionはスクロール方向を指定。※ Axis.verticalはデフォルトなので省略可能
// scrollDirection: Axis.horizontal,
// Columnは縦に並べるウィジェット
child: Column(
// Columnの場合:
// MainAxisAlignment.center -> 縦方向の親要素の中央に配置
// MainAxisAlignment.start -> 縦方向の親要素の上揃えに配置
// MainAxisAlignment.end -> 縦方向の親要素の下揃えに配置
// CrossAxisAlignment.center -> 横方向の一番広い要素の中央に配置
// CrossAxisAlignment.start -> 横方向の一番広い要素の左揃えに配置
// CrossAxisAlignment.end -> 横方向の一番広い要素の右揃えに配置
// -> CrossAxisAlignmentで要素同士の整列ではなく、全要素を整列したいなら、
// Column自体をAlignmentでラップしてtopRightなどを指定する
// Rowの場合:
// ↑の縦方向と横方向の考え方が逆になるだけ
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// 独自ウィジェット。Columnの中に並べるウィジェット1(静的なウィジェットはconstをつける)
const HellWidget(),
HellWidget2(counter: _counter),
// Textウィジェット。Columnの中に並べるウィジェット2(静的なウィジェットはconstをつける)
const Text(
'TextStyleテストTextStyleテストTextStyleテストTextStyleテストTextStyleテストTextStyleテストTextStyleテストTextStyleテストTextStyleテスト',
maxLines: 2,
// TextOverflow.ellipsisで3点リーダを表示
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.pink,
fontSize: 30,
fontWeight: FontWeight.bold,
),
),
// Textウィジェット。Columnの中に並べるウィジェット3(動的なウィジェットはconstをつけない)
Text(
'$_counter', // 変数_counterを使う
style: Theme.of(context).textTheme.headlineMedium,
),
// インターネット上の画像を表示するウィジェット
Image.network(
'https://jumpneeyan.com/wp-content/uploads/2020/10/kurapika_img_1.png',
width: 300,
// アスペクト比を保持したまま小さいのサイズを優先
height: 100,
),
// プロジェクト内の画像を表示するウィジェット
Image.asset(
'assets/kurapika.jpg',
width: 300,
height: 300,
),
// ColoredBoxは小要素に色をつける(Containerでも同じことができる)
const ColoredBox(
color: Color.fromARGB(255, 0, 255, 0),
child:
// Paddingウィジェット
// EdgeInsets.all -> 全てにパディング
// EdgeInsets.symmetric(vertical, horizontal) -> 上下または左右の指定されたものをパディング
// EdgeInsets.only(top, left, right, bottom) -> 引数省略可。指定があったものだけパディング
// EdgeInsets.zero -> パディングなし
const Padding(
padding: EdgeInsets.all(50.0),
child: Text("paddingテスト"),
),
),
// Alignは小要素の位置を好きな位置に配置できる
Align(
alignment: Alignment.centerRight,
child: Text("Alignテスト"),
),
// ElevatedButtonは影付きのボタン
ElevatedButton(
style: ElevatedButton.styleFrom(
// 角丸ボタンの場合
// shape: RoundedRectangleBorder(
// // 引数は丸める半径を指定
// borderRadius: BorderRadius.circular(5)
// )
// 円ボタンにする場合(paddingで円の大きさを大きくなる)
shape: CircleBorder(),
padding: EdgeInsets.all(34)),
onPressed: () {
print('テキストボタンを押しました');
// ダイアログを表示
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("お知らせ"),
content: Text("テキストボタンを押しました"),
actions: [
TextButton(
onPressed: () {
// ダイアログを閉じる
Navigator.of(context).pop();
},
child: Text('close'))
],
);
});
},
child: Text("これはテキストボタン1")),
// 枠線付きのテキストウィジェット
OutlinedButton(
onPressed: () {
_pushPage();
},
child: Text("次の画面へ"))
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter, // ボタンが押された時に呼び出す関数を指定
tooltip: 'Increment',
child: const Icon(Icons.add), // ボタンのアイコン.add意外にもIcons.starとかも指定できる
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
void _pushPage() {
Navigator.push(
// アプリの状況や位置を保持している情報
context,
// 画面遷移を行うための情報をまとめたもの
MaterialPageRoute(
builder: (context) {
// 遷移先のページ
return ListViewTestPage(
pageTitle: '遷移先ページのタイトル',
);
},
// モーダル繊維は以下コメントを外せばいい
// fullscreenDialog: true,
),
);
}
}
// stlと打つだけでlive templateでほとんど自動で出てくる
class HellWidget extends StatelessWidget {
const HellWidget({super.key});
@override
Widget build(BuildContext context) {
return const Text("こんにちは");
}
}
// stfと打つだけでlive templateでほとんど自動で出てくる
class HellWidget2 extends StatefulWidget {
// 引数にcounterをとる
HellWidget2({super.key, required this.counter});
int counter;
@override
State<HellWidget2> createState() => _HellWidget2State();
}
class _HellWidget2State extends State<HellWidget2> {
@override
Widget build(BuildContext context) {
return widget.counter % 2 == 0 ? const Text('おはよう') : const Text('こんばんは');
}
}
class ListViewTestPage extends StatefulWidget {
ListViewTestPage({required this.pageTitle});
final String pageTitle;
@override
State<ListViewTestPage> createState() => _ListViewTestPageState();
}
class _ListViewTestPageState extends State<ListViewTestPage> {
bool isChecked = false;
double _currentSliderValue = 20;
String inputText = "";
bool switchVal = false;
final TextEditingController _controller = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// ListViewTestPageの引数はこのようにwidget.でアクセスできる
title: Text(widget.pageTitle),
),
body: ListView(
children: [
Container(
color: Colors.cyan,
height: 100,
),
Container(
color: Colors.pink,
height: 100,
),
// ListTileはListViewのアイテムを定義
ListTile(
// 一番初めのアイコン、サムネイル画像など
leading: Icon(Icons.smoking_rooms),
// タイトル
title: Text('タイトル'),
// サブタイトル
subtitle: Text('サブタイトル'),
// 一番最後のアイコン、サムネイル画像など
trailing: Icon(Icons.arrow_forward_ios),
onTap: () {
print("tapped item");
},
),
// 削除可能なセルはDismissibleでラップする
Dismissible(
key: UniqueKey(),
onDismissed: (direction) {
// 削除処理
print("delete");
},
child: ListTile(
// 一番初めのアイコン、サムネイル画像など
leading: Icon(Icons.smoking_rooms),
// タイトル
title: Text('削除可能なセル'),
// サブタイトル
subtitle: Text('サブタイトル'),
// 一番最後のアイコン、サムネイル画像など
trailing: Icon(Icons.arrow_forward_ios),
onTap: () {
print("tapped item");
},
),
),
// チェックボックス付きのListTile。これはListView意外にもColumnの要素にも使える
CheckboxListTile(
// checkboxの位置をどこにするかを指定する
controlAffinity: ListTileControlAffinity.leading,
// タイトル
title: Text('タイトル'),
value: isChecked,
onChanged: (value) {
setState(() {
isChecked = !isChecked;
});
},
// サブタイトル
subtitle: Text('サブタイトル'),
),
Slider(
value: _currentSliderValue,
min: 0,
max: 100,
// divisionsは何段階にするかを指定
divisions: 100,
label: _currentSliderValue.round().toString(),
onChanged: (double value) {
setState(() {
_currentSliderValue = value;
});
}),
// SizedBox使い方1:余白を作る
SizedBox(height: 30.0),
// SizedBox使い方2:小ウィジェットのサイズを指定する
// TODO: widthはなぜか効かない(ListViewだから?)
SizedBox(
width: 300.0,
height: 60.0,
child: ColoredBox(
color: Colors.yellow,
child: Text('slider value is $_currentSliderValue'),
)),
TextField(
controller: _controller,
keyboardType: TextInputType.number,
decoration: InputDecoration(
labelText: 'テキストフィールド',
),
onChanged: (text) {
setState(() {
inputText = text;
});
print('First text field: $text');
},
),
Text('TextField value is $inputText'),
Switch(
value: switchVal,
onChanged: (newValue) {
setState(() {
switchVal = newValue;
});
}),
Text('Switch value is $switchVal'),
ElevatedButton(
onPressed: () {
// 元の画面に戻る処理(プッシュもモーダルもこれでOK)
Navigator.pop(context);
},
child: Text('戻る'))
],
),
);
}
}
ダークモード対応
dart main.dart
class MyApp extends StatelessWidget {
const MyApp({super.key});
ThemeData _lightTeme() {
return ThemeData(
brightness: Brightness.light,
primarySwatch: Colors.cyan,
);
}
ThemeData _darkTeme() {
return ThemeData(
brightness: Brightness.dark,
primarySwatch: Colors.cyan,
);
}
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: _lightTeme(),
darkTheme: _darkTeme(),
// ここをThemeMode.darkにするとダークモードになる
themeMode: ThemeMode.light,
home: const MyHomePage(),
);
}
}
WebView
pubspec.yamlにwebview_flutterを追加してから
class WebViewPage extends StatefulWidget {
const WebViewPage({super.key});
@override
State<WebViewPage> createState() => _WebViewPageState();
}
class _WebViewPageState extends State<WebViewPage> {
// lateは、後で初期化するという意味
late final WebViewController webViewController;
@override
void initState() {
super.initState();
webViewController = WebViewController()
// ..はカスケード記法。↑のコードを;で終わらせず、生成したオブジェクトのメソッドを使うという意味
// JavaScript有効
..setJavaScriptMode(JavaScriptMode.unrestricted)
..loadRequest(Uri.parse('https://google.com'));
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: WebViewWidget(
controller: webViewController,
),
),
);
}
}
■上部のタブバー
- DefaultTabController
https://www.kamo-it.org/blog/25/
■下部のタブバー
- BottomNavigationBar
https://zenn.dev/urasan/articles/5bb85a54fb23fb
■Expanded
https://qiita.com/nannany_stores/items/d4114f615e4d53964121
https://flutter.salon/widget/expanded/
https://zenn.dev/pressedkonbu/articles/flexible-vs-expanded
■ウィジェットを傾ける
https://note-tmk.hatenablog.com/entry/2022/08/15/221820
■svgの表示
SvgPicture
https://zenn.dev/joo_hashi/articles/c6940c20ce06f7
■ウィジェットの配置
Container(
width: 300, // ListViewの中では無効
height: 300, // ListViewの中でも有効
color: Colors.grey,
child:
Stack(
children: [
Align(
alignment: Alignment.centerLeft,
child: Container(
height: 100, // このサイズを指定しない場合、デフォルトで親と同じ高さ = 黄色のviewがそうなってる
color: Colors.blue,
child: Column(
children: <Widget>[
// Expanded(child:
Text('centerLeft1'), // デフォルトで親の左上に合わせる
// ),
// const SizedBox(height: 10),
// Expanded(child:
Text('centerLeft2'),
// ),
],
),
),
),
// const SizedBox(width: 40),
Align(
alignment: Alignment.bottomRight,
child: Container(
color: Colors.yellow,
child: Column(
children: <Widget>[
// Expanded(child:
Container(
color: Colors.red,
child: Text('bottomRight1')), // デフォルトで親の左上に合わせる
// ),
// const SizedBox(height: 10),
// Expanded(child:
Container(
color: Colors.white,
child: Text('bottomRight2')),
// ),
],
),
),
),
],
),
);