Closed25
Flutter の Cupertino Widgets を使ってみる
はじめに
このスクラップでは Cupertino Widgets の中から Material Widgets とは異なりそうなものをピックアップして使ってみる
Material Widgets 版
過去に Material Widgets を使ってみたスクラップを作成した
前回
前回は下記3点の VSCode の Flutter デバッグ機能を使ってみた
- Inspector
- Outline
- Performance
Counter
Flutter のカウンターアプリを Material → Cupertino にする
準備コマンド
プロジェクトの作り方は Material の場合と同じ
flutter create hello_cupertino
cd hello_cupertino
追加のパッケージのインストールなども必要ない
コード
全体的に変更する必要がある
hello_cupertino/lib/main.dart
import 'package:flutter/cupertino.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const CupertinoApp(
title: 'Flutter Demo',
theme: CupertinoThemeData(
brightness: Brightness.light,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text(widget.title),
trailing: CupertinoButton(
onPressed: _incrementCounter,
child: const Icon(CupertinoIcons.add),
),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text('$_counter'),
],
),
),
);
}
}
実行コマンド
flutter run
実行結果
CupertinoActionSheet
コード
main.dart
import 'package:flutter/cupertino.dart';
void main() {
runApp(const CupertinoApp(
home: MyApp(),
theme: CupertinoThemeData(brightness: Brightness.light),
));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('Navigation bar title'),
),
child: Center(
child: CupertinoButton(
onPressed: () => _showActionSheet(context),
child: const Text('Show acton sheet'),
),
),
);
}
Future<void> _showActionSheet(BuildContext context) async {
final result = await showCupertinoModalPopup<String>(
context: context,
builder: (context) {
return CupertinoActionSheet(
title: const Text('Title'),
message: const Text('Message'),
actions: <CupertinoActionSheetAction>[
CupertinoActionSheetAction(
onPressed: () {
Navigator.of(context).pop("result");
},
child: const Text('Action'),
),
],
);
},
);
print(result);
}
}
実行結果
CupertinoActivityIndicator
コード
main.dart
import 'package:flutter/cupertino.dart';
void main() {
runApp(const CupertinoApp(
home: MyApp(),
theme: CupertinoThemeData(brightness: Brightness.light),
));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('Navigation bar title'),
),
child: Center(
child: CupertinoActivityIndicator(),
),
);
}
}
実行結果
CupertinoAlertDialog
コード
main.dart
import 'package:flutter/cupertino.dart';
void main() {
runApp(const CupertinoApp(
home: MyApp(),
theme: CupertinoThemeData(brightness: Brightness.light),
));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('Navigation bar title'),
),
child: Center(
child: CupertinoButton(
child: const Text("Show dialog"),
onPressed: () async {
final answer = await showCupertinoModalPopup<String>(
context: context,
builder: (context) {
return CupertinoAlertDialog(
title: const Text('title'),
content: const Text('content'),
actions: <CupertinoDialogAction>[
CupertinoDialogAction(
onPressed: () {
Navigator.of(context).pop("no");
},
isDefaultAction: true,
child: const Text('No'),
),
CupertinoDialogAction(
onPressed: () {
Navigator.of(context).pop("yes");
},
child: const Text('Yes'),
),
],
);
},
);
print(answer);
},
),
),
);
}
}
実行結果
CupertinoButton
コード
main.dart
import 'package:flutter/cupertino.dart';
void main() {
runApp(const CupertinoApp(
home: MyApp(),
theme: CupertinoThemeData(brightness: Brightness.light),
));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('Navigation bar title'),
),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CupertinoButton(onPressed: () {}, child: const Text('Enabled')),
const SizedBox(height: 15),
const CupertinoButton(onPressed: null, child: Text('Disabled')),
const SizedBox(height: 15),
CupertinoButton.filled(onPressed: () {}, child: const Text('Enabled')),
const SizedBox(height: 15),
const CupertinoButton.filled(onPressed: null, child: Text('Disabled')),
],
),
),
);
}
}
実行結果
CupertinoContextMenu
コード
main.dart
import 'package:flutter/cupertino.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const CupertinoApp(
title: 'Flutter Demo',
theme: CupertinoThemeData(
brightness: Brightness.light,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('Navigation bar title'),
),
child: Center(
child: CupertinoContextMenu(
actions: [
CupertinoContextMenuAction(
onPressed: () {},
child: const Text('Action'),
),
],
child: Container(
color: CupertinoColors.activeBlue,
width: 100,
height: 100,
alignment: Alignment.center,
child: const Text('Text'),
),
),
),
);
}
}
実行結果
CupertinoDatePicker
コード
main.dart
import 'package:flutter/cupertino.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const CupertinoApp(
title: 'Flutter Demo',
theme: CupertinoThemeData(
brightness: Brightness.light,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('Navigation bar title'),
),
child: Center(
child: CupertinoButton.filled(
onPressed: () {
showCupertinoModalPopup(
context: context,
builder: (context) {
return Container(
height: 216,
color: CupertinoColors.systemBackground.resolveFrom(context),
child: SafeArea(
child: CupertinoDatePicker(
onDateTimeChanged: (value) {},
),
),
);
},
);
},
child: const Text('Show date picker'),
),
),
);
}
}
実行結果
CupertinoNavigationBar
コード
MaterialApp でも CupertinoNavigationBar は使える
タイトルを中央に配置したい時に便利
main.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('AppBar title')),
body: Column(
children: [
CupertinoNavigationBar(
leading: OutlinedButton(
onPressed: () {},
child: const Text('Button'),
),
middle: const Text('Title'),
),
],
),
);
}
}
実行結果
CupertinoPicker
コード
main.dart
import 'package:flutter/cupertino.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const CupertinoApp(
title: 'Flutter Demo',
theme: CupertinoThemeData(brightness: Brightness.light),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('Navigation bar title'),
),
child: Center(
child: CupertinoButton(
onPressed: () {
showCupertinoModalPopup(
context: context,
builder: (context) {
return Container(
height: 216,
color: CupertinoColors.systemBackground.resolveFrom(context),
child: SafeArea(
child: CupertinoPicker(
itemExtent: 32,
onSelectedItemChanged: (value) {},
children: List.generate(3, (index) => const Text('Item')),
),
),
);
},
);
},
child: const Text('Show picker'),
),
),
);
}
}
実行結果
メモ
SafeArea って何だろうと思って調べたらわかりやすい記事があった
iPhone 画面の上端や下端にあるものを上手に避けてくれるらしい
CupertinoPopupSurface
コード
main.dart
import 'package:flutter/cupertino.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const CupertinoApp(
title: 'Flutter Demo',
theme: CupertinoThemeData(brightness: Brightness.light),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('Navigation bar title'),
),
child: Center(
child: CupertinoButton(
onPressed: () {
showCupertinoModalPopup(
context: context,
builder: (context) {
return CupertinoPopupSurface(
child: Container(
height: 200,
color: CupertinoColors.systemBackground.resolveFrom(context),
child: Center(
child: CupertinoButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Close'),
),
),
),
);
},
);
},
child: const Text('Show picker'),
),
),
);
}
}
実行結果
メモ
実装手順の素晴らしい動画を見つけた
CupertinoScrollbar
コード
main.dart
import 'package:flutter/cupertino.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const CupertinoApp(
title: 'Flutter Demo',
theme: CupertinoThemeData(brightness: Brightness.light),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final ScrollController _scrollController = ScrollController();
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('Navigation bar title'),
),
child: CupertinoScrollbar(
thumbVisibility: true,
controller: _scrollController,
child: ListView.builder(
controller: _scrollController,
itemCount: 100,
itemBuilder: (context, index) {
return const Padding(
padding: EdgeInsets.all(10),
child: Text('Title'),
);
},
),
),
);
}
}
実行結果
メモ
ScrollController を Scrollbar と ListView の両方に設定しないとエラーになる
CupertinoSearchTextField
コード
main.dart
import 'package:flutter/cupertino.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const CupertinoApp(
title: 'Flutter Demo',
theme: CupertinoThemeData(brightness: Brightness.light),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return const CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('Navigation bar title'),
),
child: Center(
child: CupertinoSearchTextField(),
),
);
}
}
実行結果
CupertinoSegmentedControl
コード
main.dart
import 'package:flutter/cupertino.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const CupertinoApp(
title: 'Flutter Demo',
theme: CupertinoThemeData(brightness: Brightness.light),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('Navigation bar title'),
),
child: Center(
child: CupertinoSegmentedControl<String>(
onValueChanged: (value) {
},
children: {
for (final segment in ["1", "2", "3"])
segment: Padding(
padding: const EdgeInsets.all(10),
child: Text(segment),
),
},
),
),
);
}
}
実行結果
メモ
実際に使う場合は StatefulWidget
, setState
, groupValue
を使う
CupertinoSlider
コード
main.dart
import 'package:flutter/cupertino.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const CupertinoApp(
title: 'Flutter Demo',
theme: CupertinoThemeData(brightness: Brightness.light),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
double slider = 1;
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('Navigation bar title'),
),
child: Center(
child: CupertinoSlider(
min: 0,
max: 10,
value: slider,
onChanged: (value) {
setState(() {
slider = value;
});
},
),
),
);
}
}
実行結果
CupertinoSlidingSegmentedControl
コード
main.dart
import 'package:flutter/cupertino.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const CupertinoApp(
title: 'Flutter Demo',
theme: CupertinoThemeData(brightness: Brightness.light),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String _segment = "1";
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('Navigation bar title'),
),
child: Center(
child: CupertinoSlidingSegmentedControl<String>(
groupValue: _segment,
onValueChanged: (value) {
setState(() {
_segment = value!;
});
},
children: {
for (final segment in ["1", "2", "3"])
segment: Padding(
padding: const EdgeInsets.all(10),
child: Text(segment),
),
},
),
),
);
}
}
実行結果
メモ
CupertinoSegmentedControl
とほぼ同じ
CupertinoSliverNavigationBar
コード
main.dart
import 'package:flutter/cupertino.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const CupertinoApp(
title: 'Flutter Demo',
theme: CupertinoThemeData(brightness: Brightness.light),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return const CupertinoPageScaffold(
child: CustomScrollView(
slivers: [
CupertinoSliverNavigationBar(
leading: Icon(CupertinoIcons.person),
largeTitle: Text('Navigation bar title'),
trailing: Icon(CupertinoIcons.plus),
)
],
),
);
}
}
実行結果
メモ
下記の記事が興味深い
CupertinoSwitch
コード
main.dart
import 'package:flutter/cupertino.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const CupertinoApp(
title: 'Flutter Demo',
theme: CupertinoThemeData(brightness: Brightness.light),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _value = true;
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('Navigation bar title'),
),
child: Center(
child: CupertinoSwitch(
value: _value,
onChanged: (value) {
setState(() {
_value = value;
});
},
),
),
);
}
}
実行結果
CupertinoTabScaffold + Bar + View
コード
main.dart
import 'package:flutter/cupertino.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const CupertinoApp(
title: 'Flutter Demo',
theme: CupertinoThemeData(brightness: Brightness.light),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return CupertinoTabScaffold(
tabBar: CupertinoTabBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.search_circle_fill),
label: 'Explore',
),
],
),
tabBuilder: (context, index) {
return CupertinoTabView(
builder: (context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('Page 1 of tab $index'),
),
child: Center(
child: CupertinoButton(
onPressed: () {
Navigator.of(context).push(
CupertinoPageRoute<void>(
builder: (context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('Page 2 of tab $index'),
),
child: Center(
child: CupertinoButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Back'),
),
));
},
),
);
},
child: const Text('Next page'),
),
),
);
},
);
},
);
}
}
実行結果
メモ
タブを変更した時に元のタブのページがちゃんと保存されているのがすごい
CupertinoTextField
コード
main.dart
import 'package:flutter/cupertino.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const CupertinoApp(
title: 'Flutter Demo',
theme: CupertinoThemeData(brightness: Brightness.light),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late TextEditingController _textEditingController;
void initState() {
super.initState();
_textEditingController = TextEditingController(text: "initial text");
}
void dispose() {
_textEditingController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('Navigation bar title'),
),
child: Center(
child: CupertinoTextField(
controller: _textEditingController,
),
),
);
}
}
実行結果
メモ
inital text
を設定するのがとても大変
CupertinoTimerPicker
コード
main.dart
import 'package:flutter/cupertino.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const CupertinoApp(
title: 'Flutter Demo',
theme: CupertinoThemeData(brightness: Brightness.light),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('Navigation bar title'),
),
child: Center(
child: CupertinoButton(
onPressed: () {
showCupertinoModalPopup(
context: context,
builder: (context) {
return Container(
color: CupertinoColors.systemBackground.resolveFrom(context),
height: 216,
child: CupertinoTimerPicker(
onTimerDurationChanged: (value) {},
),
);
},
);
},
child: const Text('Show time picker'),
),
),
);
}
}
実行結果
おわりに
以上で一旦クローズ、次は何を調べようかな
つづき
Flutter の carousel_slider を使ってみる、のスクラップを作成しました
このスクラップは2023/01/10にクローズされました