# Flutter
- éå€ã¡ã¢
- --- User Interface ---
- ãŠã£ãžã§ããã®åºæ¬
- ã¬ã€ã¢ãŠã
- Creating adaptive & responsive apps
- Building adaptive apps
- ã¬ã€ã¢ãŠãã®èŠä»¶(Constraints)ãç解ãã
- State ãã©ãã§ç®¡çããã
- ã¢ã»ãããšç»å
- Navigator 1.0
- Navigator 2.0
- Navigator 2.0 (routemaster)
- Deep linking
- URL æŠç¥
- Animations
- --- Data & backend ---
- State management
- Provider package
- State 管çã®éžæè¢
- Riverpod
- JSON & serialization
- HTTP éä¿¡ã®æ¹æ³
# éå€ã¡ã¢
- ãããžã§ã¯ãåã«ã¢ã³ããŒã¹ã³ã¢ããããšèµ·åã§ããªãããïŒ
- ããã©ãŒãã³ã¹èšæž¬ã¯ãªãªãŒã¹ã¢ãŒãã§è¡ãããšããããã°ã¢ãŒãã§ã¯æ§èœãèœã¡ãããã
_
ã§ååãå§ãŸãå€æ°ã¯ãã©ã€ããŒãã«ãªã- Anonymous function ãé¢æ°ã«æž¡ãæ¹æ³
some_func(() {/* ããã«åŠçãèšèŒ */})
- DevTools ã®äœ¿çšæ¹æ³
flutter run -d chrome
㧠flutter ãéå§ããws://
ããå§ãŸãã¢ããªã®ã¢ãã¬ã¹ãã³ããŒãã- VSCode ã§
Dart: Open DevTools
ãéžã³ãå çšã®ã¢ãã¬ã¹ãã³ããŒããŠConnect
ãã¿ã³ãã¯ãªãã¯ãã
- æåã®åã蟌ã¿ã¯ä»¥äžã®ããã«ããã
final myNumber = 123; final message = "hello$a"; // æ¥æ¬èªåã®å Žåã¯åºåããææ§ãªã®ã§{}ãçç¥ããªãã»ãããããã final message = "hello${a}";
# --- User Interface ---
# ãŠã£ãžã§ããã®åºæ¬
# Hello world
ãããã«æ§æ
void main() {
runApp(
const Center(
child: Text(
'Hello, world!',
textDirection: TextDirection.ltr,
),
),
);
}
- root widget ã¯ç»é¢å šäœãå ããä»æ§ã«ãªã£ãŠããã詳现ã¯åŸè¿°ã® constraints ãåç §ã
StatelessWidget
åã¯StatefulWidget
ãç¶æ¿ãããŠã£ãžã§ãããçµã¿åãããŠç»é¢ãäœã£ãŠããã- Widget ã®äž»ãã圹å²ã¯
build()
ã¡ãœãããå®è£ ããããšã
# åºæ¬ãŠã£ãžã§ãã
Text
- ããã¹ã
Column
|Row
- 瞊ã»æšªæ¹åã«èŠçŽ ã䞊ã¹ã
- web ã§ãã flexbox
Stack
- è€æ°ã®èŠçŽ ãåºçŸé ã« z æ¹åã«éããŠè¡šç€ºãã
Positioned
widget ã§äœçœ®ã調ç¯ã§ãã- web ã§ãã absolute é 眮
Container
- ããã£ã³ã°ãããŒãžã³ãããŒããŒãèæ¯è²ã䜿ãããæã«å©çšãã
BoxDecoration
ã§è£ 食ããã- web ã§ãã div
Expanded
- ã¹ããŒã¹ã䜿ãåããŸã§æ¡åŒµããããŸãã¯åãŸãããã«çž®å°ããã
child
ã®äžã§äœ¿ãflex
ã«ããæ¡åŒµçãèšå®ã§ããã
widget ãåŒæ°ãšããŠå¥ã® Widget ã«äžãã(React ã® children ã«çžåœ)ã®ã¯äŸ¿å©ã§åŒ·åãªãã¯ããã¯ã§ããã
# ãããªã¢ã«ã³ã³ããŒãã³ãã®å©çš
- Material design ã䜿ãã«ã¯ä»¥äžã®èšå®ãäºãè¡ã£ãŠããããš
# pubspec.yaml
name: my_app
flutter:
uses-material-design: true
MaterialApp
ãŠã£ãžã§ããã®åœ¹å²Navigator
ãã»ããã¢ãããã- Navigator 㯠routes ã管çããããã«äœ¿ããroutes 㯠Widget ã®ã¹ã¿ãã¯ãšããŠç®¡çãããŠãããåã ã® Widget(åç»é¢)ã¯æååã§èå¥ãããã
- ãããªã¢ã«ãã¶ã€ã³ã®ãŠã£ãžã§ããã䜿ããšãã«ã¯å¿ ãå ç¥ã«å¿ èŠã
MaterialApp
ãæäžäœã«é 眮ããåç»é¢ãScaffold
ãŠã£ãžã§ããã§äœãã®ãã°ãããã©ã¯ãã£ã¹ã
void main() {
runApp(MaterialApp(
title: 'Flutter Tutorial',
home: TutorialHome(),
));
}
class TutorialHome extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: ...,
body: ...,
floatingActionButton: ...,
);
}
}
# åãŠã£ãžã§ãããžåŒæ°ãæž¡ã
class MyText extends StatelessWidget {
// ã¡ã³ãå€æ°ã®å®çŸ©
final String firstName, lastName;
const MyText({
// åŒæ°ãåãåºããŠã¡ã³ãå€æ°ã«ã»ãã
// å¿
é ã®ãã®
required this.firstName,
// å¿
é ã§ãªããã®
this.lastName = "",
// keyã«ã€ããŠã¯ãããããããsuperãåŒã¶ãšãã«æž¡ãããã
Key? key,
}) : super(key: key);
Widget build(BuildContext context) {
return Center(child: Text(firstName + ' ' + lastName));
}
}
// 䜿ãæ¹
MyText(
firstName: 'John',
lastName: 'Doe',
)
# ãžã§ã¹ãã£ãŒ
- ãã¿ã³ãªã©ã®å Žåã¯
onPressed()
ãªã©ãçšæãããŠããã®ã§ããã䜿ãã - ãããããªãå Žåã¯
GestureDetector
ã§èŠçŽ ãã©ããããã
GestureDetector(
onTap: ..., // ã¿ããæã«è¡ãããåŠç
child: ..., // ãã¿ã³ãªã©
)
# StatefulWidget
- State ãçæãä¿æããããšã®ã§ããç¹å¥ãª widget
StatefulWidget
ãšState
ãå¥ãªããžã§ã¯ãã§ããçç±ïŒ- StatefulWidget ã¯äžæçãªãã®ãåæåã®ãã³ã«åçæãããã
- State ã¯ç¶ç¶çãªãã®ãåæåãããŠãåããã®ã䜿ãåãã(
createState()
ã¯ååã®ã¿åŒã°ãã)ãç¶æ ãä¿æããã
- StatefulWidget ã®å 容ãæžãããStatelessWidget ã«æœåºããŠããããšãéèŠ
- Stateful| Stateless ã¯é¢ä¿ãªã話ã ããããäžäœã® Widget ã§å€ã管çãããšããããšã¯ããã®å€ã®å¯¿åœã䌞ã°ãããšãæå³ããã極端ãªè©±ã
runApp()
ã«æž¡ãããã³ã³ããŒãã³ãã§ç®¡çãããå€ã¯ãã¢ããªãèµ·åããŠããéäžããã£ãšä¿æãããã
class Counter extends StatefulWidget {
final String name;
// ãã®ã¯ã©ã¹ã¯èŠªããäžããããåŒæ°ãä¿æãã
// - åŒæ°ã¯stateã®buildã¡ãœãããã`widget.***`ãšããŠã¢ã¯ã»ã¹å¯èœ
// - åŒæ°ã¯åžžã«finalãšããŠæ±ããã
const Counter({this.name = 'my counter', Key? key}) : super(key: key);
_CounterState createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _counter = 0;
void _increment() {
setState(() {
// setStateã®äžã§å€ãå€æŽããããšã§ç»é¢ãåæåãããã
_counter++;
});
}
build(context) {
return Column(
children: <Widget>[
RaisedButton(
onPressed: _increment,
child: Text('Increment'),
),
// `StatefulWidget`ã®ã¡ã³ãå€æ°ãšstateãçµã¿åãããŠäœ¿çšã§ãã
Text(widget.name + _counter.toString()),
],
);
}
};
# widget ã®å€æŽæ€ç¥
StatefulWidget ã®åŒæ°ã State ã§å©çšããŠããå ŽåïŒwidget.***
ã䜿ã£ãŠããå ŽåïŒããããã®å€æŽã¯èªåçã«åæ ããããããæåã§å€æŽãæ€ç¥ããŠãªã«ãããããšãã¯didUpdateWidget
ããªãŒããŒã©ã€ãããã
class _CounterState extends State<Counter> {
void didUpdateWidget(Counter oldWidget) {
super.didUpdateWidget(oldWidget);
// åŠçãããã«æžã
}
}
# ã©ã€ããµã€ã¯ã«ã¡ãœãã
StatefulWidget ã«ã¯ã©ã€ããµã€ã¯ã«ã¡ãœããããããinitState
ãdispose
ããªãŒããŒã©ã€ãããŠèšè¿°ããã
class _CounterState extends State<Counter> {
// ããŠã³ãæã«1床ã ãè¡ãããåŠç
void initState() {
super.initState();
// åŠçãããã«æžã
}
// ã¢ã³ããŠã³ãæã«è¡ãããåŠç
void dispose() {
// åŠçãããã«æžã
super.dispose();
}
}
# Local key
- Loacl key ã䜿ãããšã§ãäŸãã°ç¡éãªã¹ãçã«ãããŠãWidget ã®åå©çšãå¹ççã«è¡ãããæ§ã«ãªãã
ValueKey
--- æ°å€ãªã©ã®å€ã§åºå¥ããObjectKey
--- ãªããžã§ã¯ãã® id ã§åºå¥ããUniqueKey
--- 絶察ã«éè€ããªãããããæå®ããã³ã³ããŒãã³ãã¯å¿ ãæ¯åäœãçŽãããã
MyTextField(key: ValueKey(2))
MyTextField(key: ValueKey("asdf"))
MyTextField(key: ObjectKey(someObject))
MyTextField(key: Uniquekey())
- Local key ãæå®ããªãå Žåãwidget ã®åå©çšã¯ã³ã³ããŒãã³ãã®äœçœ®ãšçš®é¡ã«åºã¥ããŠè¡ãããã
- äŸãã°äžèšã®ã³ãŒãã§ãshowFirst ã true ãã false ã«ãªã£ãæã«ã¯ä»¥äžã®åŠçãè¡ãããããããã¯éå¹çã§ãããæ¬æ¥ã¯ 1 ãåé€ããã ãã§ããããã
- 2 ã®èŠçŽ ãåé€ããã
- 1 ã®èŠçŽ ã®åŒæ°ã
'world'
ã«å€æŽãã
if (showFirst) MyWidget('hello'), // 1
MyWidget('world'), // 2
# Global key
- State ã¯ã©ã¹ã® state ãã¡ãœããã«å¯Ÿããå€éšããã¢ã¯ã»ã¹ããããšãã«äœ¿ãïŒãããªããšããŠããããïŒïŒ
- åœç¶ãªãã Stateful widget ã«å¯ŸããŠã®ã¿äœ¿çšã§ããããŸãã察象㮠State ã¯ã©ã¹ãStateãã¡ã³ãå€æ°ãªã©ã Pubric ã§ããå¿ èŠãããã
- ãã®åç» (opens new window)ãèŠããšããããããã
class HomeScreen extends StatelessWidget {
// 1. ã°ããŒãã«ããŒãçšæãã
final counterGlobalKey = GlobalKey<CounterState>();
void someParentFunction() {
// 3. 芪åŽããåã®Stateãã¡ãœããã«ã¢ã¯ã»ã¹ã§ããããã«ãªã
print(counterGlobalKey.currentState?.count);
counterGlobalKey.currentState?.increment();
}
build(BuildContext context) {
// 2. ã°ããŒãã«ããŒãStatefulWidgetã«äžãã
return Counter(key: counterGlobalKey);
}
}
class Counter extends StatefulWidget {
const Counter({Key? key}) : super(key: key);
createState() => CounterState();
}
// Must be public
class CounterState extends State<Counter> {
// Must be public
int count = 0;
// Must be public
void increment() {
setState(() {
count += 1;
});
}
/* 以äžçç¥ */
}
# ã¬ã€ã¢ãŠã
- Flutter ã«ãããŠã¯ã»ãŒå
šãŠã®ãã®ããŠã£ãžã§ããã§ããã
- ã¬ã€ã¢ãŠãã®ããã®ãŠã£ãžã§ãã ---
Row
ãCenter
ãªã© - UI ãšã¬ã¡ã³ããäœãããã®ãŠã£ãžã§ããïŒç®ã«èŠãããŠã£ãžã§ããïŒ ---
Text
ãRaisedButton
ãªã©
- ã¬ã€ã¢ãŠãã®ããã®ãŠã£ãžã§ãã ---
# 1 ã€ã®ãŠã£ãžã§ãããé 眮ããæé
ãŸããã¬ã€ã¢ãŠããŠã£ãžã§ãã (opens new window)ãéžã¶
Center(child:null)
次ã«ãç®ã«èŠãããŠã£ãžã§ãããäœæãããäŸãã°Text
ãIcon
ãªã©ã
Text('hello')
ç®ã«èŠãããŠã£ãžã§ãããã¬ã€ã¢ãŠããŠã£ãžã§ããã«è¿œå ãããã¬ã€ã¢ãŠããŠã£ãžã§ããã®child
åã¯children
ã«èšèŒããããšã§è¡ãã
Center(child:Text('hello'))
ãŠã£ãžã§ãããããŒãžã«é 眮ããã
# è€æ°ã®ãŠã£ãžã§ãããé 眮ããæé
Row
ãColumn
ã䜿ã£ãŠè€æ°ã® widget ã瞊åã¯æšªæ¹åã«äžŠã¹ãŠé
眮ã§ããã
Row(
// `justify-content`çžåœ
mainAxisAlignment: MainAxisAlignment.spaceAround,
// `align-items`çžåœ
crossAxisAlignment: CrossAxisAlignment.center,
children: [
/* ...䞊ã¹ããwidgets */
]
)
- äžèšã®ãããªæœè±¡åºŠã®é«ããŠã£ãžã§ãããçšæãããŠããã®ã§é©å®æŽ»çšããããšã
- Row ã®ä»£ããã«äœ¿ãã
ListTile
(opens new window)- è¡é æ«ã«ã¢ã€ã³ã³ãè¿œå ãããã3 è¡ãŸã§ã®ããã¹ãã衚瀺ããããšãç°¡åã«è¡ãã
- Column ã®ä»£ããã«äœ¿ãã
ListView
(opens new window)- ã«ã©ã ã¬ã€ã¢ãŠããç°¡åã«äœæã§ãããã³ã³ãã³ãã瞊ã«ããµããå Žåã¯èªåã§ã¹ã¯ããŒã«ã衚瀺ãããã
- Row ã®ä»£ããã«äœ¿ãã
# Row ã Column ãã¯ã¿åºãå Žå
- ç»åçã倧ããããŠç»é¢å ã«èŠçŽ ãåãŸããªãå Žåãé»è²ãšé»è²ã®ããŒããŒãèŠåãšããŠç»é¢äžã«è¡šç€ºãããã
- ç»é¢å
ã«åãããå Žåã¯
Expanded
ã§Row
çã®ååèŠçŽ ãå²ãã flex
ãæå®ããããšã§æ¡å€§çãèšå®ã§ããã
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Image.asset('images/pic1.jpg'),
),
Expanded(
flex: 2,
child: Image.asset('images/pic2.jpg'),
),
Expanded(
child: Image.asset('images/pic3.jpg'),
),
],
);
# è©°ããŠè¡šç€ºãã
ããã©ã«ãã§ã¯Row
ãColumn
㯠main axis ã®æ¹åã«æ倧éæ¡å€§ãããæ¡å€§ããã«è©°ããŠè¡šç€ºãããå Žåã¯mainAxisSize
ãèšå®ããã
Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.star),
Icon(Icons.star),
Icon(Icons.star),
],
);
# èªã¿ãããã³ãŒãã«ãã
- Flutter ã®ã³ãŒãã¯ããã¹ããæ·±ããªããšããã«èªã¿ã¥ãããªãã
- UI ã®ãŸãšãŸãããšã«å€æ°ãé¢æ° ã«åãåºãããšãå¿ãããããšã
# ç»å
ãããã¯ãŒã¯ãã
Image.network('http://lorempixel.com/400/200/');
ããŒã«ã«ãã
# pubspec.yaml
flutter:
assets:
- images/pic1.jpg
- images/pic2.jpg
- images/pic3.jpg
Image.asset('images/pic1.jpg');
# ãã䜿ãã¬ã€ã¢ãŠãçšã®ãŠã£ãžã§ãã
- ãŸãã¯ãããã®åºæ¬çãªãŠã£ãžã§ããã䜿ã£ãŠç»é¢ãäœã£ãŠã¿ããã
- ãŠã£ãžã§ããã«ã¿ãã° (opens new window)
- Standard widgets ãš Material widgets ããããåŸè 㯠MaterialApp å ã§ãã䜿ããªãã
# Container
- ããã£ã³ã°ãããŒãžã³ãããŒããŒã䜿ãæ
- èæ¯è²ãèæ¯ç»åãå€ãããæ
- åäžã®åãæã€
# GridView
- ãŠã£ãžã§ãããæ Œåç¶ã«é 眮ãããæ
- ã¯ã¿åºããéšåã¯èªåã§ã¹ã¯ããŒã«å¯èœã«ãªã
GridView.count
æå®ããæ°ã§çžŠæšªãåå²ããGridView.extent
ã¢ã€ãã ã®æå€§å¹ ãæå®ããŠåå²ãã
# ListView
- ãªã¹ã圢åŒã§é ç®ã䞊ã¹ããšãã«äœ¿ã
- 瞊æ¹åã暪æ¹åã®ã©ã¡ãã§ã䜿çšã§ãã
- ã¯ã¿åºãå Žåã¯èªåçã«ã¹ã¯ããŒã«å¯èœã«ãªã
Column
ããèšå®é ç®ã¯å°ãªããã®ã®ã䜿ãããããã¹ã¯ããŒã«ãèªåçã«èšå®ãããã
# Stack
- ãŠã£ãžã§ããïŒå€ãã®å Žåã¯ç»åïŒã«éããŠãŠã£ãžã§ãããé 眮ãããšãã«äœ¿ã
- 1 ã€ç®ã«æå®ãããŠã£ãžã§ãããããŒã¹ãŠã£ãžã§ãããšãªãã2 ã€ç®ä»¥éã«æå®ãããŠã£ãžã§ããããã®äžã«éããŠè¡šç€ºãããã
- ã¹ã¯ããŒã«ã¯äžå¯
- ã¯ã¿ã ããéšåã衚瀺ãããã©ããã¯éžæã§ãã
# Card (Material widget)
- ãããªã¢ã«ãã¶ã€ã³ã®ã«ãŒãã䜿ããããšã
- é¢é£ããæ å ±ããŸãšãŸãã«ããããšã
- äžžè§ãšåœ±ãã€ã
- ã¹ã¯ããŒã«ã¯äžå¯
- åæãµã€ãºã¯ 0x0 ãªã®ã§æ³šæã
SizedBox
ã䜿ããšãµã€ãºãæå®ã§ããã ListTile
ãšçµã¿ããããŠäœ¿ãããšãå€ã
# ListTile (Material widget)
- ãããªã¢ã«ãã¶ã€ã³ã®ãªã¹ãã¿ã€ã« (opens new window)ã䜿ããããšã
- 3 è¡ãŸã§ã®ããã¹ããšãè¡é or è¡æ«ã®ä»»æã®ã¢ã€ã³ã³ããæ§æããã
Row
ããèšå®é ç®ã¯å°ãªãããç°¡åã«äœ¿ãããšãã§ãã
# çªå€ç·š
- Widget ã¯ãããŸã§èšèšå³ã»èšå®ã»é圢ã§ãããå®éã«ã¯ Widget ãåºã«ã㊠Element ãšããå®äœãäœæããããããç»é¢äžã«é 眮ãããã
- Element tree ã web ã§èšããšããã® DOM tree ã«ããããWidget tree ãä»®æ³ DOM ã«ãããã€ã¡ãŒãžããªïŒ
InheritedWidget
ã䜿ããšãReact ã® Context ã®ãããªããšãå®çŸã§ããã詳ããã¯ãã¡ã (opens new window)ãèªããšããããããã
# Creating adaptive & responsive apps
TODO: äžæŠãã¹
# Building adaptive apps
TODO: äžæŠãã¹
# ã¬ã€ã¢ãŠãã®èŠä»¶(Constraints)ãç解ãã
- Constraint ãšã¯ä»¥äžã®ïŒã€ã®ããšã
- min width
- max width
- min height
- max height
# ã¬ã€ã¢ãŠãã® 3 åå
- Flutter ã®ã¬ã€ã¢ãŠãã¯**Constraints go down. Sizes go up. Parent sets position.**ã®ïŒã€ã®ã«ãŒã«ã§åäœãããweb ã®ã¬ã€ã¢ãŠããšã¯å
šãç°ãªãã«ãŒã«ãªã®ã§èŠæ³šæãå
·äœçãªåäœã¯ä»¥äžã®éãã
- ãŠã£ãžã§ããã¯ãparentããconstraintãåãåãã
- ãŠã£ãžã§ããã¯ãchildrenã«constraintãäŒããããã§ãchildren ãã©ãããsizeã«ãªããããèãã
- ãŠã£ãžã§ããã¯ãchildrenãäžã€äžã€é 眮(position)ããŠããã
- æåŸã«ããŠã£ãžã§ããèªèº«ãã©ãããsizeã«ãªãããã芪ã«äŒããã
# ã¬ã€ã¢ãŠãã®å¶çŽäºé
- ãŠã£ãžã§ããã¯åžæãããµã€ãºã«ãªããªãå Žåãããããªããªã芪ã®æ±ºãã constraint ãåªå ãããããã
- ãŠã£ãžã§ãã㯠position ãèªåã§æ±ºããããªãããç¥ãããšãã§ããªãããªããªããããã決ããã®ã¯èŠªã ããã
- ãŠã£ãžã§ããã® size ã position ãæ£ç¢ºã«èšå®ããããã«ã¯ãããªãŒå šäœãææ¡ããå¿ èŠãããããªããªãã芪㮠size ã position ããã®ãŸã芪ã«äŸåããããã
- alignment ã¯å ·äœçã«èšå®ããå¿ èŠãããããªããªãããã child ã parent ãšã¯éããµã€ãºã«ãªãããã®ã«ãparent ãååãªæ å ±ãæã£ãŠããªãå Žåãchild ã®ãµã€ãºã¯ç¡èŠãããäºãããããã
# ã¬ã€ã¢ãŠãã®æåãäŸã§åŠã¶
ãã®ããŒãž (opens new window)ã® Examples ãçºããŠãããšã ãããåããã以äžããããšããããšãªã¹ãïŒ
- ãµã€ãºæ±ºå®ããžãã¯ã¯æå€ãšè€éãªå Žåãããã®ã§æ³šæãäŸãã°
Container
ãªããåèŠçŽ ãããã°æå°ãµã€ãºããªããã°æ倧ãµã€ãºã«ãªããªã©ã ConstrainedBox
ã¯åã« constraint ãæ¶æ¥µçã«å ãããèšãæãããšãããšããšèŠªããäžããããŠãã constraint ãããå Žåã¯ãã¡ããåªå ãããã®ã§æ³šæãUnconstrainedBox
ã¯åã«ãããªãå¶çŽãå ããªããããåãèªç¶ãªãµã€ãºã§æåã§ããOverflowBox
ã¯ãåäœã¯UnconstrainedBox
ãšåãã ããé»è²ããŒãã©ã®èŠåã衚瀺ããªãç¹ãç°ãªãã- bounded ãšããèšèã®æå³ã¯ãwidth ãš height ã«å€ãæã£ãŠãããšããããšã
é㯠unbounded ã§ãwidth ãš height ã
double.infinity
ã§ãããšããããšã Row
ã¯åã«ãããªã constraint ãå ããªããããèªç¶ã®ãµã€ãºã§è¡šç€ºãããããã ããExpanded
ã§åãã©ããããå Žåãåã®ãµã€ãºã¯ç¡èŠãããã
# Tight vs loose constraints
- Tight constraint ---
FlexFit.tight
- (min|max)Width ã«åãå€ãæå®ããã〠(min|max)Height ã«ãåãå€ãæå®ããããš
- äŸïŒroot screen ã® constraint: åèŠçŽ ã«ç»é¢ãµã€ãºãšåãã«ãªãããã«åŒ·å¶ããããã®å ŽåãåèŠçŽ 㧠width ãæå®ããŠãç¡èŠãããã
- Loose constraint ---
FlexFit.loose
- max(Width|Height)ã®ã¿æå®ããmin(Width|Height)㯠0 ã§ããããš
- äŸïŒCenter ã® constraint: åèŠçŽ ã¯å°ãããªã£ãŠããããã ãç»é¢ãµã€ãºã¯è¶ ããªãããã«ã
Expanded ãš Flexible ã®éã
Flexible
--- ããã©ã«ãã§ã¯fit:FlexFit.loose
ã§ãããåã«çž®å°ãèš±å¯ãããExpanded
---Flexible
ãfit:FlexFit.tight
ãããã®ãšç䟡ãåã®ãµã€ãºãç¡èŠããã
Center
ãªã©ã®ããã€ãã® Widget ã¯ãconstraints ãç·©ãã(loosen)ããå¹æãæã€ã
# Box constraints
Flutter ã®å
éšã§ã¯RenderBox
ãšãããªããžã§ã¯ããçšã㊠widget ãç»é¢ã«é
眮ããŠããã
- ãªãã¹ã倧ãããªãããšãã Box
äŸïŒ
Center
,ListView
- ãªãã¹ã倧ãããªãããšãã Box
äŸïŒ
- children ãšåããµã€ãºã«ãªãããšãã Box
äŸïŒ
Transform
,Opacity
- children ãšåããµã€ãºã«ãªãããšãã Box
äŸïŒ
- ç¹å®ã®ãµã€ãºã«ãªãããšãã Box
äŸïŒ
Image
,Text
- ç¹å®ã®ãµã€ãºã«ãªãããšãã Box
äŸïŒ
- ç¹æ®ãª Box
äŸïŒ
Container
--- ããã©ã«ã㧠1ãåãããã° 2ãwidth ãæå®ããã° 3 ã«ãªã äŸïŒRow|Column
--- 芪ããåãåã£ã Constraints ã«ãã£ãŠå€ããïŒåŸè¿°ïŒ
unbounded ã«é¢ãã泚æ
- unbounded(æ倧å¹
ãé«ããç¡é倧) 㪠constraint ãšäžèšã® 1 ãçµã¿åããããšãšã©ãŒã«ãªãã
Row|Column
ãšã¹ã¯ããŒã«å¯èœãªãŠã£ãžã§ããListView|ScrollView
ãå€ãªæ¹åã§çµã¿åããããšãã«èµ·ãããã¡ãªã®ã§æ³šæããã
- Flex box(
Row|Column
)ã¯ãbounded ã unbounded ãã«ããæåãå€ããã- bounded ãªãããã®ãµã€ãºãŸã§æ倧éæ¡åŒµãã
- unbounded ãªããåã®ãµã€ãºãŸã§çž®å°ããããã®ãšãã
flex
ããããã£ãExpanded
ã³ã³ããŒãã³ãã¯åœç¶ãªãã䜿ããªãã
# State ãã©ãã§ç®¡çããã
State ã®ç®¡çã«ã¯ 3 ã€ã®ã¢ãããŒããããã
- ãŠã£ãžã§ããèªèº«ã§ç®¡çãã
- ã¢ãã¡ãŒã·ã§ã³ã®ç¶æ ãªã©ã芪ãç¥ãããããªãæ å ±ã¯ãŠã£ãžã§ããèªèº«ã§ç®¡çãããšããã
- 芪ã管çãã
- ãã§ãã¯ããã¯ã¹ã®ãã§ãã¯ç¶æ ãã€ã³ãããæ¬ã®ããã¹ããªã©ããŠãŒã¶ã®å ¥åããŒã¿ã¯èŠªã管çãããšè¯ãã
- äžèšã® 1 ãšïŒãé©å®çµã¿åããã
- ã¢ãã¡ãŒã·ã§ã³ã®ç¶æ ã¯ãŠã£ãžã§ããèªèº«ã§ç®¡çãããŠãŒã¶ã®å ¥åããŒã¿ã¯èŠªã管çãããªã©ã
# ã¢ã»ãããšç»å
äžèšã®é ç®ãèšèŒãããŠãããå¿ èŠã«ãªã£ããèªãã
- ç»åã®èªã¿èŸŒã¿æ¹æ³
- ç»åã® Variant (Dark or Light)ãæ±ãæ¹æ³
- 解å床ã®ç°ãªãç»åãèªã¿èŸŒãæ¹æ³
- json ãªã©ã®ããã¹ããã¡ã€ã«ãèªã¿èŸŒãæ¹æ³
# Navigator 1.0
Navigator
- Route ãªããžã§ã¯ããã¹ã¿ãã¯ãšããŠç®¡çãã widget
Route
- åç»é¢ãè¡šçŸãããªããžã§ã¯ã
MaterialPageRoute
ã¯ã©ã¹çã«ãã£ãŠå®è£ ãããããšãå€ã- Named routes ãš Anonymous routes ããã
# Anonymous routes
ä»»æã®ã¿ã€ãã³ã°ã§äžæçã«ç»é¢ãäœæããŠè¡šç€ºããããšã
// é·ç§»ããããšã
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return OtherScreen();
}),
);
// æ»ããšãã¯
Navigator.pop(context);
# Named routes
- äºåå®çŸ©ããååä»ãã®ç»é¢ã«é·ç§»ããããšã
- åŒæ°ãç»é¢ã«æž¡ãããšãå¯èœ (opens new window)
- ããããurl ãããŒã¹ããŠå€ãååŸããããšã¯ã§ããªããäŸïŒ
/details/:itemId
// ã«ãŒãã£ã³ã°èšå®
MaterialApp(
routes: {
'/': (context) => HomeScreen(),
'/details': (context) => DetailScreen(),
},
);
// é·ç§»ããããšã
Navigator.pushNamed(
context,
'/details',
);
// æ»ããããšã
Navigator.pop(context);
# onGenerateRoute
- æãæè»ãª route ã®äœææ¹æ³ã
- Url ãããŒã¹ããŠå€ãååŸããç»é¢ã«æž¡ãããšãå¯èœ
MaterialApp(
onGenerateRoute: (settings) {
// Handle '/'
if (settings.name == '/') {
return MaterialPageRoute(builder: (context) => HomeScreen());
}
// Handle '/details/:id'
var uri = Uri.parse(settings.name);
if (uri.pathSegments.length == 2 &&
uri.pathSegments.first == 'details') {
var id = uri.pathSegments[1];
return MaterialPageRoute(builder: (context) => DetailScreen(id: id));
}
// ãã®ä»
return MaterialPageRoute(builder: (context) => UnknownScreen());
},
);
# Navigator 2.0
- Navigator 2.0 ã¯ããŸãã«ãè€éãªã®ã§ããã®ãŸãŸäœ¿ãã®ã¯å±éºããªããFlutter éçºããŒã ããããã£ãè©äŸ¡ãèªèããŠãããããç°¡æãªããã±ãŒãžã®éçºãå§ãŸã£ãŠããããšã¯ãããweb ã®å Žå㯠URL ãšã®åæãå¿ èŠãšãªãããšãå€ãã®ã§ãv2.0 ãã»ãŒå¿ é ãšãªãã
- Navigator 2.0 ãå¿ èŠãªå Žåã¯ãµãŒãããŒãã£ã®ã©ãããŒã©ã€ãã©ãªã䜿ããšããã2021/08 çŸåšã§ã¯routemaster (opens new window)ã䜿ãè¯ãããã
åèè³æ
- https://zenn.dev/ntaoo/articles/6641e846765da1
- https://medium.com/flutter/learning-flutters-new-navigation-and-routing-system-7c9068155ade
# Navigator 2.0 (routemaster)
routemaster ã䜿ãããšã§ãNavigator2.0 ãåçã«ã·ã³ãã«ã«æ±ãããšãã§ãããããã ã¿ããžã®å¯Ÿå¿ãªã©ãç°¡åã«è¡ããã詳现㯠routemaster ã® ããŒãž (opens new window) ãåç §ã
final routes = RouteMap(
routes: {
'/': (_) => CupertinoTabPage(
child: HomePage(),
paths: ['/feed', '/settings'],
),
'/feed': (_) => MaterialPage(child: FeedPage()),
'/settings': (_) => MaterialPage(child: SettingsPage()),
'/feed/profile/:id': (info) => MaterialPage(
child: ProfilePage(id: info.pathParameters['id'])
),
}
);
void main() {
runApp(
// ããã¯å®åæ
MaterialApp.router(
routerDelegate: RoutemasterDelegate(routesBuilder: (context) => routes),
routeInformationParser: RoutemasterParser(),
),
);
}
// é·ç§»ããããšãã¯ä»¥äžã®ããã«ãã
Routemaster.of(context).push('/feed/profile/1');
# Deep linking
ã¢ãã€ã«ã ãã«é¢ä¿ãã話ã®ãããªã®ã§äžæŠãã¹
# URL æŠç¥
- web ã®éçºã«ãããŠãURL ã®åœ¢åŒããããã·ã¥ããããšãããã·ã¥ãªããããéžã¶ããšãã§ãããããã©ã«ãã¯ããã·ã¥ãããå€æŽãããå Žåã¯ãã¡ã (opens new window)ãåèã«èšå®ããã
- å¿ èŠãããã° Base URL ã®èšå®ãè¡ãã
# Animations
ãã¹
# --- Data & backend ---
# State management
ä»ã®ãªã¢ã¯ãã£ã or 宣èšçãªç°å¢(React ãªã©)ã®ç¥èŠãããã°ãã®ç« ã¯ã¹ãããããŠãããšã®ããšãªã®ã§ãäžéšã®ã¿æç²ã
- çšèª
- Ephemeral state --- ã¢ãã¡ãŒã·ã§ã³ã®ç¶æ ãªã©
- App state --- ãŠãŒã¶ãå ¥åäžã®æåãªã©
# Provider package
- æãã·ã³ãã«ãª State ã®ç®¡çæ¹æ³ãReact ã® Context ã«äŒŒãŠããã
InheritedWidget
,InheritedNotifier
,InheritedModel
ãšããäœã¬ãã«ãªãã®ã䜿ãããããããã®- provider ã䜿ãã«ã¯ä»¥äžã®ïŒã€ã®ããšãç解ããå¿
èŠããã
- ChangeNotifier
- ChangeNotifierProvider
- Consumer
# ChangeNotifier
notifyListeners()
ã«ãããåå«ã«å€æŽãéç¥ãã圹å²ãæã€- éã«èšããšãConsumer 㯠ChangeNotifier ã賌èªããããšãã§ãã
flutter:foundation
ç±æ¥
class CartModel extends ChangeNotifier {
final List<Item> _items = [];
get items => ListView(_items);
int get totalPrice => _items.length * 12345;
void add(Item item) {
_items.add(item);
notifyListeners(); // éç¥
}
void removeAll() {
_items.clear();
notifyListeners(); // éç¥
}
}
# ChangeNotifierProvider
ChangeNotifier
ã®ã€ã³ã¹ã¿ã³ã¹ãåå«ã«æž¡ã圹å²ãæã€- æãè¿ãå ±éã®ç¥å ã«é 眮ãã
provider
ããã±ãŒãžç±æ¥
ChangeNotifierProvider(
create: (context) => CartModel(),
child: const CommonAncestor(),
)
// è€æ°ã®Notifierã䜿ãããå Žåã¯ä»¥äžã®ããã«ãã
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => CartModel()),
Provider(create: (context) => SomeOtherClass()),
],
child: const CommonAncestor(),
),
# Comsumer
- Generic ã¯å¿ é ãåæ å ±ãåºã«ãã©ã® Notifier ã®å€ãååŸãããã決å®ãããããã
- child ã¯ããã©ãŒãã³ã¹æé©åã®ããã«äœ¿ãããã詳现ã¯ãã¡ã (opens new window)ãåç §ã
Consumer<CartModel>(
builder: (context, cart, child) {
return Text("Total price: ${cart.totalPrice}");
},
);
- ãªããã¡ãœããã ãã«ã¢ã¯ã»ã¹ã§ããã°è¶³ããå Žåã¯ã
Consumer
ã ãšã³ã¹ããé«ãã€ããããProvider.of
ãlisten: false
ã«ããŠäœ¿ããšè¯ãã
Provider.of<CartModel>(context, listen: false).removeAll();
# State 管çã®éžæè¢
- setState
- åå§çãEphemeral state ã®ç®¡çã«æé©ã
- InheritedWidget & InheritedModel
- ç¥å ãšåå«ã®é㧠state ãããåãããããã®äœã¬ãã«ãªææ³ã
- provider ã¯å®è³ªçã«ã¯ãããã䜿ã£ãŠãã
- Redux
- 説æäžèŠ
- Fish-Redux
- çç¥ãäžãã倧èŠæš¡ãªã¢ããªã«æé©ãšã®ããšã
- BLoC / Rx
- Stream, Observable ããŒã¹
- GetIt
- Service locator ããŒã¹
BuildContext
ãäžèŠ
- MobX
- Observable, reaction ããŒã¹
- Flutter Commands
- Command ãã¿ãŒã³
ValueNotifiers
ã䜿ã£ãŠãã- GetIt ãšçµã¿åãããã®ããããã
- Binder
- recoid ã«ã€ã³ã¹ãã€ã¢ãããŠãã
InheritedWidget
ã䜿ã£ãŠãã
- GetX
- Riverpod
- provider ã䜿ããããæ¹é ãããã®ã
- Flutter SDK ãžã®äŸåãäžåãªã
- states_rebuilder
- dependency injection ã«ããç¶æ 管ç + integrated router ããæ§æããã
# Riverpod
Riverpod (opens new window)ã®ç¹åŸŽ
- ãšã©ãŒãã³ã³ãã€ã«æç¹ã§æ€åºã§ãã
- ãã¹ãããªããã
- ãã¹ãå¯èœã§ãã
# JSON & serialization
JSON ã®ã·ãªã¢ã©ã€ãºã»ãã·ãªã¢ã©ã€ãºãã©ããããïŒ
- PoC ãªã©ã®å°ããªãããžã§ã¯ãã§ã¯ manual serialization ãæé©
dart:convert
ã䜿ã- çã® JSON ã
jsonDecode()
ã«äžãããšMap<String, dynamic>
ãåŸãããã - 容æã«ã©ã³ã¿ã€ã ãšã©ãŒãèµ·ããã®ã§æ³šæ
- äžèŠæš¡ã»å€§èŠæš¡ãªãããžã§ã¯ãã§ã¯ Code generation ãæé©
json_serializable
ãbuild_value
ãªã©ã®å€éšã©ã€ãã©ãªã䜿ã£ãŠè¡ã- ã³ã³ãã€ã«æç¹ã§ãšã©ãŒãã§ãã¯ãå¯èœ
- ã¢ãã«ã¯ã©ã¹ãç£èŠããèªåçã«ãšã³ã³ãŒãã£ã³ã°çšã®ã³ãŒããçæãã
# æåã·ãªã¢ã©ã€ãº (inline)
- ã¿ã€ãã«ãã容æã«ãšã©ãŒãèµ·ãããã
- åæ å ±ã倱ãããŠãã
Map<String, dynamic> user = jsonDecode(jsonString);
print('Howdy, ${user['name']}!');
print('We sent the verification link to ${user['email']}.');
# æåã·ãªã¢ã©ã€ãº (inside model classes)
- ã¢ãã«ã«ãšã³ã³ãŒãã»ãã³ãŒãã®æ©èœãããããæ¹æ³
- åæ å ±ã¯èšå®ããããã®ã®ãåé·ã§ããã以äžã®è€éåã«ã¯èããããªãã
class User {
final String name;
final String email;
User(this.name, this.email);
User.fromJson(Map<String, dynamic> json)
: name = json['name'],
email = json['email'];
Map<String, dynamic> toJson() => {
'name': name,
'email': email,
};
}
# Code generation ã«ããã·ãªã¢ã©ã€ãº
以äžãjson_serializable ã䜿ã£ãäŸãèšèŒããŸããpubspec.yaml
ã®èšå®ãè¡ãã
dependencies:
json_annotation: <latest_version>
dev_dependencies:
build_runner: <latest_version>
json_serializable: <latest_version>
ã¢ãã«ã¯ã©ã¹ã json_serializable ã¯ã©ã¹ã«çœ®ãæããã
// user.dart
import 'package:json_annotation/json_annotation.dart';
// Userã¯ã©ã¹ãèªåçæãããã³ãŒãã«ã¢ã¯ã»ã¹ããããã®ããŸããªãã
// çæããããã¡ã€ã«åã§ãã`<å
ã®ãœãŒã¹ãã¡ã€ã«å>.g.dart`ãæå®ããã
part 'user.g.dart';
// json_serializableã®åŠç察象ã«ãã
()
class User {
User(this.name, this.email);
String name;
String email;
// ããŸããªã
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
// ããŸããªã
Map<String, dynamic> toJson() => _$UserToJson(this);
}
ãµãŒããµã€ããšããã³ããµã€ãã§ããŒåãéãå Žåãªã©ã¯ãäžèšã®ããã«ããã
// ãã£ãŒã«ãããšã«èšå®ããæ¹æ³
(name: 'registration_date_millis')
final int registrationDateMillis;
// äžæ¬ã§èšå®ããæ¹æ³(JSONã®ã¹ããŒã¯ã±ãŒã¹<=>ã¢ãã«ã®ãã£ã¡ã«ã±ãŒã¹)
(fieldRename: FieldRename.snake)
ãã®ä»ãå¿ é èŠä»¶ãªã©ãèšå®ã§ãã
(defaultValue: false) // ãªããã°ãã®å€ãã»ãããã
(required: true) // ãªããã°ãšã©ãŒãäžãã
(ignore: true) // ã³ãŒãèªåçæã®åŠç察象ããå€ã
ã³ãŒãã®èªåçææ¹æ³
- äžåã®ã¿
flutter pub run build_runner build
- ç¶ç¶ããŠãŠã©ãã
flutter pub run build_runner watch
å®éã®å©çšæ¹æ³ã¯ä»¥äžã®éããããã§ããã¯ãã·ãªã¢ã©ã€ãºã«é¢ãã責åã¯ã©ã€ãã©ãªã«å§è²ãããã
// ãã³ãŒãã£ã³ã°
Map<String, dynamic> userMap = jsonDecode(jsonString);
var user = User.fromJson(userMap);
// ãšã³ã³ãŒãã£ã³ã°
var userMap = User.toJson(user);
String json = jsonEncode(userMap);
ãªããã¢ãã«ããã¹ããããããšãå¯èœããã ãããã¹ãããã¢ãã«ã¯ JSON ã«ãããšãã«ããã©ã«ãã§ã¯å±éãããªãã®ã§ãexplicitToJson
ã®æå®ãå¿
èŠã詳现ã¯ãã¡ã (opens new window)ã
(explicitToJson: true)
# HTTP éä¿¡ã®æ¹æ³
äžèšã䜿ãã https://github.com/dart-lang/http