見出し画像

マテリアルデザインの縮むヘッダーをFlutterで爆速で実装する

この記事は有料で設定していますが、続きはありません。
投げ銭感覚で購入していただけたらと思います。

今回はマテリアルデザインによくある縮むヘッダーを作ってきます。
Droid kaigiアプリにも使用されています。

完成品

完成品はこちら

前提


前提としてFlutterの開発環境を作成しておきましょう。

上の記事は英語ですが、頑張って読んでいただいて環境構築していただけたらと思います。

サーバーサイドの開発環境を作るより圧倒的に楽です。

プロジェクトを作成

プロジェクトを作成したら、色々コメントが書かれたプログラムが書かれていますが無駄なので今回は消してしまいましょう。

画面の真ん中に「Hello World」って表示します。

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Material App"),
      ),
      body: new Center(
        child: new Text(
              'Hello World',
            ),
      ),
    );
  }
}


ListViewを作成する

次にListViewを作成します。

表示する数は仮で10とします。

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Material App"),
      ),
      body: new Center(
        child: ListView(
          children: List.generate(10, (index){
            return Card(
              child: ListTile(
                title: Text("child$index"),
                leading: Icon(Icons.person),
              ),
            );
          }),
        ),
      ),
    );
  }
}

遷移先の画面を作成する

ここまできたらListにタップイベント追加して、画面遷移処理まで作成してしまいましょう。

タップイベントはInkWellクラスをCardにクラスの上にラップしましょう。

Cardのところにカーソルを当ててランプのマークをクリックして「wrap with new Widget」をクリックするとWidgetでラップするのをショートカットできます。

一気に画面遷移処理まで作成しましょう。

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Material App"),
      ),
      body: new Center(
        child: ListView(
          children: List.generate(10, (index) {
            return InkWell(
              child: Card(
                child: ListTile(
                  title: Text("child$index"),
                  leading: Icon(Icons.person),
                ),
              ),
              onTap: () {
                Navigator.push(
                    context,
                    MaterialPageRoute(
                        builder: (context) => MyHomePageDetail()));
              },
            );
          }),
        ),
      ),
    );
  }
}

class MyHomePageDetail extends StatefulWidget {
  @override
  _MyHomePageDetailState createState() => new _MyHomePageDetailState();
}

class _MyHomePageDetailState extends State<MyHomePageDetail> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text("Material App"),
        ),
        body: Center(
          child: Text("画面遷移できました"),
        ));
  }
}

これで画面遷移処理まで完成です。

そこからボタンを押したタイトルを次の画面に渡すように実装しましょう。
コードは下記で実装できます

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Material App"),
      ),
      body: new Center(
        child: ListView(
          children: List.generate(10, (index) {
            return InkWell(
              child: Card(
                child: ListTile(
                  title: Text("child$index"),
                  leading: Icon(Icons.person),
                ),
              ),
              onTap: () {
                Navigator.push(
                    context,
                    MaterialPageRoute(
                        builder: (context) => MyHomePageDetail("child$index")));
              },
            );
          }),
        ),
      ),
    );
  }
}

class MyHomePageDetail extends StatefulWidget {

  MyHomePageDetail(this._title);
  final String _title;

  @override
  _MyHomePageDetailState createState() => new _MyHomePageDetailState(_title);
}

class _MyHomePageDetailState extends State<MyHomePageDetail> {

  _MyHomePageDetailState(this._title);
  final String _title;

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text("Material App"),
        ),
        body: Center(
          child: Text("$_title"),
        ));
  }
}

縮むレイアウトを作成する

いよいよここから縮むレイアウトを作成します。

先にコードを載せます。

class _MyHomePageDetailState extends State<MyHomePageDetail> {
  _MyHomePageDetailState(this._title);

  final String _title;

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        body: CustomScrollView(
      slivers: <Widget>[
        SliverAppBar(
          pinned: true,
          expandedHeight: 200.0,
          floating: false,
          flexibleSpace: FlexibleSpaceBar(
            title: Text(_title),
            background: Container(
              color: Colors.green,
            ),
          ),
        )
      ],
    ));
  }
}

Appbarは取り除いてしまい、bodyの中でAppbarを定義するような形になります。

CustomScrollViewをbodyに定義し、その中にSliverAppBarを定義します。slivers属性の配列は全て「Sliver~~~」から始まるクラス以外を定義するとクラッシュするので注意が必要です。

ひとまずこれで実行したレイアウトはこうなります。

これだけで十分縮むヘッダーは作成できているのでですが、どうせならListViewを表示させてみましょう。

SliverListを作成する

さっきも書きましたが、customScrollViewのslivers属性は「Sliver~~」から始まるWidget以外で書くとクラッシュするので注意が必要です(自分もこれにめちゃくちゃハマりました)

ですのでSliverListをつかいましょう。

ここでは仮てitemを100件表示しています。

class _MyHomePageDetailState extends State<MyHomePageDetail> {
  _MyHomePageDetailState(this._title);

  final String _title;

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        body: CustomScrollView(
      slivers: <Widget>[
        SliverAppBar(
          pinned: true,
          expandedHeight: 200.0,
          floating: false,
          flexibleSpace: FlexibleSpaceBar(
            title: Text(_title),
            background: Container(
              color: Colors.green,
            ),
          ),
        ),
        SliverList(delegate: SliverChildBuilderDelegate((context, index) {
          return Card(
            child: ListTile(
              title: Text("List:$index"),
              leading: Icon(Icons.person),
            ),
          );
        },childCount: 100)),
      ],
    ));
  }
}

これで完成です。

宣伝

少しでも投げ銭いただけると次書くモチベーションが上がるのでお願いいたします。

Twitter↓ これからもFlutterについて呟くのフォローお願いします。

ここから先は

0字

¥ 300

投げ銭はいりません。それより無料でできる拡散をしてください!! 感想をツイートしていただけることが一番嬉しいです!!