Upgrade to Pro — share decks privately, control downloads, hide ads and more …

[Flutter Meetup Tokyo #1] 雰囲気でFlutterやってみた

Da5a59469ce3ebb55619ce34f85f8c4f?s=47 syarihu
April 18, 2018

[Flutter Meetup Tokyo #1] 雰囲気でFlutterやってみた

Flutter Meetup Tokyo #1 の発表資料です。

Da5a59469ce3ebb55619ce34f85f8c4f?s=128

syarihu

April 18, 2018
Tweet

Transcript

  1. 雰囲気でFlutterやってみた Flutter Meetup Tokyo #1 2018/04/18 (Wed) @syarihu

  2. Taichi Sato (@syarihu) • Money Forward ◦ Android Engineer •

    TechBooster
  3. Taichi Sato (@syarihu) • Android開発歴 7年くらい

  4. Taichi Sato (@syarihu) • Android開発歴 7年くらい • Flutter歴

  5. Taichi Sato (@syarihu) • Android開発歴 7年くらい • Flutter歴 3日くらい

  6. Flutter 気になるけど よくわからん…

  7. そうだ、雰囲気でやってみよう

  8. 昔つくった個人アプリ(の一部)を Flutterで作ってみた

  9. 愛工大シャトルバス時刻表

  10. None
  11. None
  12. None
  13. None
  14. None
  15. これを Flutterで 作ってみる

  16. やること • Widgetに分解 • Widgetを配置 • APIから取得した結果を Widgetに反映

  17. 1. Widgetに分解

  18. Toolbar

  19. AppBar Toolbar

  20. ViewPager

  21. ViewPager TabBarView

  22. ListView

  23. ListView ListView

  24. Bottom NavigationView

  25. Bottom NavigationView Bottom NavigationView

  26. 2. Widgetを配置

  27. AppBar

  28. AppBar void main() => runApp(new MyApp()); class MyApp extends StatelessWidget

    { @override Widget build(BuildContext context) { return new MaterialApp( title: '愛工大シャトルバス時刻表', theme: new ThemeData( primarySwatch: Colors.blue, ), home: new MyHomePage(title: '愛工大シャトルバス時刻表 '), ); } }
  29. AppBar void main() => runApp(new MyApp()); class MyApp extends StatelessWidget

    { @override Widget build(BuildContext context) { return new MaterialApp( title: '愛工大シャトルバス時刻表', theme: new ThemeData( primarySwatch: Colors.blue, ), home: new MyHomePage(title: '愛工大シャトルバス時刻表 '), ); } }
  30. AppBar void main() => runApp(new MyApp()); class MyApp extends StatelessWidget

    { @override Widget build(BuildContext context) { return new MaterialApp( title: '愛工大シャトルバス時刻表', theme: new ThemeData( primarySwatch: Colors.blue, ), home: new MyHomePage(title: '愛工大シャトルバス時刻表 '), ); } }
  31. AppBar void main() => runApp(new MyApp()); class MyApp extends StatelessWidget

    { @override Widget build(BuildContext context) { return new MaterialApp( title: '愛工大シャトルバス時刻表', theme: new ThemeData( primarySwatch: Colors.blue, ), home: new MyHomePage(title: '愛工大シャトルバス時刻表 '), ); } }
  32. AppBar

  33. AppBar class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) :

    super(key: key); final String title; @override _MyHomePageState createState() => new _MyHomePageState(); }
  34. AppBar class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) :

    super(key: key); final String title; @override _MyHomePageState createState() => new _MyHomePageState(); }
  35. AppBar class _MyHomePageState extends State<MyHomePage> { @override Widget build(BuildContext context)

    { return new Scaffold( appBar: new AppBar( title: new Text(widget.title), ), body: new Center(), ); } }
  36. AppBar

  37. TabBarView

  38. TabBarView class _MyHomePageState extends State<MyHomePage> { ... @override Widget build(BuildContext

    context) {
  39. TabBarView return new DefaultTabController( length: 2, child: new Scaffold( appBar:

    new AppBar( title: new Text(widget.title), bottom: new TabBar( tabs: <Widget>[ new Tab(text: "愛工大ゆき"), new Tab(text: "八草駅ゆき"), ], ), ), body: new TabBarView( children: _pages, ))); } }
  40. TabBarView return new DefaultTabController( length: 2, child: new Scaffold( appBar:

    new AppBar( title: new Text(widget.title), bottom: new TabBar( tabs: <Widget>[ new Tab(text: "愛工大ゆき"), new Tab(text: "八草駅ゆき"), ], ), ), body: new TabBarView( children: _pages, ))); } }
  41. TabBarView return new DefaultTabController( length: 2, child: new Scaffold( appBar:

    new AppBar( title: new Text(widget.title), bottom: new TabBar( tabs: <Widget>[ new Tab(text: "愛工大ゆき"), new Tab(text: "八草駅ゆき"), ], ), ), body: new TabBarView( children: _pages, ))); } }
  42. TabBarView return new DefaultTabController( length: 2, child: new Scaffold( appBar:

    new AppBar( title: new Text(widget.title), bottom: new TabBar( tabs: <Widget>[ new Tab(text: "愛工大ゆき"), new Tab(text: "八草駅ゆき"), ], ), ), body: new TabBarView( children: _pages, ))); } }
  43. TabBarView final List<Widget> _pages = <Widget>[ Column( children: <Widget>[ Padding(

    padding: const EdgeInsets.all(32.0), child: new Text("widget1"), ) ], ), Column( children: <Widget>[ Padding( padding: const EdgeInsets.all(32.0), child: new Text("widget2"), ) ], ), ];
  44. TabBarView

  45. None
  46. None
  47. None
  48. 自分で作るしか無さそう…

  49. 独自TabBarを作るには? • TabBarはPreferredSizeWidgetを実装し ている • PreferredSizeWidgetを実装したクラスな らいけそう

  50. CustomTabBar class CustomTabBar extends StatelessWidget implements PreferredSizeWidget { final TabBar

    tabBar; final String currentDate; final String currentTimeTable; CustomTabBar({ this.currentDate, this.currentTimeTable, this.tabBar }) : super();
  51. CustomTabBar class CustomTabBar extends StatelessWidget implements PreferredSizeWidget { final TabBar

    tabBar; final String currentDate; final String currentTimeTable; CustomTabBar({ this.currentDate, this.currentTimeTable, this.tabBar }) : super();
  52. CustomTabBar class CustomTabBar extends StatelessWidget implements PreferredSizeWidget { final TabBar

    tabBar; final String currentDate; final String currentTimeTable; CustomTabBar({ this.currentDate, this.currentTimeTable, this.tabBar }) : super();
  53. CustomTabBar class CustomTabBar extends StatelessWidget implements PreferredSizeWidget { final TabBar

    tabBar; final String currentDate; final String currentTimeTable; CustomTabBar({ this.currentDate, this.currentTimeTable, this.tabBar }) : super();
  54. CustomTabBar @override Size get preferredSize => new Size(0.0, 90.0); @override

    Widget build(BuildContext context) { return new Column( children: <Widget>[ createCurrentStatus(), tabBar ], ); }
  55. CustomTabBar @override Size get preferredSize => new Size(0.0, 90.0); @override

    Widget build(BuildContext context) { return new Column( children: <Widget>[ createCurrentStatus(), tabBar ], ); }
  56. CustomTabBar @override Size get preferredSize => new Size(0.0, 90.0); @override

    Widget build(BuildContext context) { return new Column( children: <Widget>[ createCurrentStatus(), tabBar ], ); }
  57. CustomTabBar @override Size get preferredSize => new Size(0.0, 90.0); @override

    Widget build(BuildContext context) { return new Column( children: <Widget>[ createCurrentStatus(), tabBar ], ); }
  58. CustomTabBar Widget createCurrentStatus() { return Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[

    createExpanded(currentDate), createExpanded(currentTimeTable), ], ); }
  59. CustomTabBar Widget createCurrentStatus() { return Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[

    createExpanded(currentDate), createExpanded(currentTimeTable), ], ); }
  60. CustomTabBar Widget createCurrentStatus() { return Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[

    createExpanded(currentDate), createExpanded(currentTimeTable), ], ); } Widget A (currentDate) Widget B (currentTimeTable)
  61. CustomTabBar Expanded createExpanded(String text) { return new Expanded( flex: 1,

    child: new Container( decoration: new BoxDecoration( border: new Border.all(color: Colors.white) ), child: ...
  62. CustomTabBar Expanded createExpanded(String text) { return new Expanded( flex: 1,

    child: new Container( decoration: new BoxDecoration( border: new Border.all(color: Colors.white) ), child: ...
  63. CustomTabBar Expanded createExpanded(String text) { return new Expanded( flex: 1,

    child: new Container( decoration: new BoxDecoration( border: new Border.all(color: Colors.white) ), child: ...
  64. CustomTabBar child: new Padding( padding: const EdgeInsets.all(2.0), child: new MaterialButton(

    onPressed: () {}, child: new Text( text, style: new TextStyle( color: Colors.white ), ), ), ), ), ); }
  65. CustomTabBar child: new Scaffold( appBar: new AppBar( title: new Text(widget.title),

    bottom: new CustomTabBar( currentDate: "2018年4月16日(月)", currentTimeTable: "Aダイヤ", tabBar: new TabBar( tabs: <Widget>[ new Tab(text: "愛工大ゆき"), new Tab(text: "八草駅ゆき"), ], ), ), ),
  66. CustomTabBar child: new Scaffold( appBar: new AppBar( title: new Text(widget.title),

    bottom: new CustomTabBar( currentDate: "2018年4月16日(月)", currentTimeTable: "Aダイヤ", tabBar: new TabBar( tabs: <Widget>[ new Tab(text: "愛工大ゆき"), new Tab(text: "八草駅ゆき"), ], ), ), ),
  67. CustomTabBar child: new Scaffold( appBar: new AppBar( title: new Text(widget.title),

    bottom: new CustomTabBar( currentDate: "2018年4月16日(月)", currentTimeTable: "Aダイヤ", tabBar: new TabBar( tabs: <Widget>[ new Tab(text: "愛工大ゆき"), new Tab(text: "八草駅ゆき"), ], ), ), ),
  68. CustomTabBar child: new Scaffold( appBar: new AppBar( title: new Text(widget.title),

    bottom: new CustomTabBar( currentDate: "2018年4月16日(月)", currentTimeTable: "Aダイヤ", tabBar: new TabBar( tabs: <Widget>[ new Tab(text: "愛工大ゆき"), new Tab(text: "八草駅ゆき"), ], ), ), ),
  69. CustomTabBar

  70. ListView

  71. ListView static List<String> createDataList() { List<String> list = List(); for

    (int i = 0; i < 100; i++) { list.add("test$i"); } return list; }
  72. ListView static List<String> createDataList() { List<String> list = List(); for

    (int i = 0; i < 100; i++) { list.add("test$i"); } return list; }
  73. ListView static List<Card> createCards(List<String> list) { List<Card> cardList = new

    List(); list.forEach((s) { cardList.add(new Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Text(s), ))); }); return cardList; }
  74. ListView static List<Card> createCards(List<String> list) { List<Card> cardList = new

    List(); list.forEach((s) { cardList.add(new Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Text(s), ))); }); return cardList; }
  75. ListView static ListView createListView() { return new ListView( shrinkWrap: true,

    padding: const EdgeInsets.all(20.0), children: createCards(createDataList()), ); }
  76. ListView static ListView createListView() { return new ListView( shrinkWrap: true,

    padding: const EdgeInsets.all(20.0), children: createCards(createDataList()), ); }
  77. ListView final List<Widget> _pages = <Widget>[ createListView(), createListView(), ];

  78. ListView

  79. 3. APIから取得して  Widgetに反映

  80. APIからデータを取得する • モデルを作る • http.getで取ってくる • 結果をWidgetに反映

  81. None
  82. http.getで取ってくる Future<Destinations> fetchPost() async { final response = await http.get(

    'http://api.syarihu.net/ait_timetable/timetables/'); final responseJson = json.decode(response.body); return new Destinations.fromJson(responseJson); }
  83. http.getで取ってくる Future<Destinations> fetchPost() async { final response = await http.get(

    'http://api.syarihu.net/ait_timetable/timetables/'); final responseJson = json.decode(response.body); return new Destinations.fromJson(responseJson); }
  84. http.getで取ってくる Future<Destinations> fetchPost() async { final response = await http.get(

    'http://api.syarihu.net/ait_timetable/timetables/'); final responseJson = json.decode(response.body); return new Destinations.fromJson(responseJson); }
  85. 結果をWidgetに反映 static FutureBuilder<Destinations> createFutureBuilder( int destination) { return new FutureBuilder<Destinations>(

    future: fetchPost(), builder: (context, snapshot) { if (snapshot.hasData) { return createListView( snapshot.data.destinations[destination].timetables); } else if (snapshot.hasError) { return new Text("${snapshot.error}"); } return new CircularProgressIndicator(); }, ); }
  86. 結果をWidgetに反映 static FutureBuilder<Destinations> createFutureBuilder( int destination) { return new FutureBuilder<Destinations>(

    future: fetchPost(), builder: (context, snapshot) { if (snapshot.hasData) { return createListView( snapshot.data.destinations[destination].timetables); } else if (snapshot.hasError) { return new Text("${snapshot.error}"); } return new CircularProgressIndicator(); }, ); }
  87. 結果をWidgetに反映 static FutureBuilder<Destinations> createFutureBuilder( int destination) { return new FutureBuilder<Destinations>(

    future: fetchPost(), builder: (context, snapshot) { if (snapshot.hasData) { return createListView( snapshot.data.destinations[destination].timetables); } else if (snapshot.hasError) { return new Text("${snapshot.error}"); } return new CircularProgressIndicator(); }, ); }
  88. 結果をWidgetに反映 static FutureBuilder<Destinations> createFutureBuilder( int destination) { return new FutureBuilder<Destinations>(

    future: fetchPost(), builder: (context, snapshot) { if (snapshot.hasData) { return createListView( snapshot.data.destinations[destination].timetables); } else if (snapshot.hasError) { return new Text("${snapshot.error}"); } return new CircularProgressIndicator(); }, ); }
  89. 結果をWidgetに反映 static FutureBuilder<Destinations> createFutureBuilder( int destination) { return new FutureBuilder<Destinations>(

    future: fetchPost(), builder: (context, snapshot) { if (snapshot.hasData) { return createListView( snapshot.data.destinations[destination].timetables); } else if (snapshot.hasError) { return new Text("${snapshot.error}"); } return new CircularProgressIndicator(); }, ); }
  90. 結果をWidgetに反映 class _MyHomePageState extends State<MyHomePage> { final List<Widget> _pages =

    <Widget>[ new ConstrainedBox( constraints: const BoxConstraints.expand(), child: createFutureBuilder(0), ), new ConstrainedBox( constraints: const BoxConstraints.expand(), child: createFutureBuilder(1), ), ];
  91. 結果をWidgetに反映 class _MyHomePageState extends State<MyHomePage> { final List<Widget> _pages =

    <Widget>[ new ConstrainedBox( constraints: const BoxConstraints.expand(), child: createFutureBuilder(0), ), new ConstrainedBox( constraints: const BoxConstraints.expand(), child: createFutureBuilder(1), ), ];
  92. APIから取得してWidgetに反映

  93. まとめ

  94. まとめ • 標準のWidgetならわりと簡単になんでも 作れそう • ちょっとでも凝ったもの作ろうとすると Widgetをカスタマイズしないと ダメそう

  95. まとめ • 通信の部分もWidgetで作るとは思わな かったので本当にすべてはWidget だった(小並感) • そんなに時間をかけずにある程度作れた ので慣れればかなり良さそう

  96. 技術書典4 • 日時 2018/04/22 (日) 11:00〜17:00 • 場所 秋葉原UDX アキバ・スクエア

  97. NOW & FUTURE TechBooster • Android • Flutter • Swift

    • Kotlin のコルーチン の動作原理など モバイル技術に注目した 解説書です
  98. ありがとうございました