👠

CustomScrollViewを使ってみた

2023/07/07に公開

SNS風のUIを作るのに使ってみた

SNS風と書いてますが、オシャレではないです😅

今回はこちらのWidgetを使用
https://api.flutter.dev/flutter/widgets/CustomScrollView-class.html
スライバーを使用してカスタムスクロールエフェクトを作成するScrollView。

CustomScrollView を使用すると、リスト、グリッド、拡大ヘッダーなどの様々なスクロールエフェクトを作成するためにスリバーを直接指定することができます。例えば、拡張アプリバーに続いてリストとグリッドを含むスクロールビューを作成するには、3つのスリバーのリストを使用します:SliverAppBar、SliverList、SliverGrid です。

これらのスリバー内のウィジェットは、RenderSliver オブジェクトを生成する必要があります。

スクロールビューの初期スクロールオフセットを制御するには、ScrollController.initialScrollOffset プロパティを設定したコントローラを提供します。

APIのデータをUIに表示してるので追加
https://pub.dev/packages/http

実際に使ってみる

APIからデータを取得するときに、使うモデルクラス

モデルクラス
model.dart
// postsからデータを取得するモデルクラス
class Post {
  final String title;
  final String body;

  Post({required this.title, required this.body});

  factory Post.fromJson(Map<String, dynamic> json) {
    return Post(
      title: json['title'],
      body: json['body'],
    );
  }
}
// albumsからデータを取得するモデルクラス
class Album {
  final String title;

  Album({required this.title});

  factory Album.fromJson(Map<String, dynamic> json) {
    return Album(
      title: json['title'],
    );
  }
}

CustomScrollViewを使用して、作成した複雑なUI

SNS風のUI
main.dart
import 'package:custom_view/model.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

void main() {
  runApp(SNSApp());
}

class SNSApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'SNS UI',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: SNSHomePage(),
    );
  }
}

class SNSHomePage extends StatefulWidget {
  const SNSHomePage({super.key});

  
  _SNSHomePageState createState() => _SNSHomePageState();
}

class _SNSHomePageState extends State<SNSHomePage> {
  // Postモデルクラスのリスト
  List<Post> posts = [];
  // Albumモデルクラスのリスト
  List<Album> albums = [];

  
  void initState() {
    super.initState();
    // initState()メソッドでfetchData()メソッドを呼び出す
    fetchData();
  }
  // APIからデータを取得するメソッド
  Future<void> fetchData() async {
    final postsResponse =
        await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));
    final albumsResponse = await http
        .get(Uri.parse('https://jsonplaceholder.typicode.com/albums'));

    if (postsResponse.statusCode == 200 && albumsResponse.statusCode == 200) {
      setState(() {
        posts = (json.decode(postsResponse.body) as List<dynamic>)
            .map((json) => Post.fromJson(json))
            .toList();
        albums = (json.decode(albumsResponse.body) as List<dynamic>)
            .map((json) => Album.fromJson(json))
            .toList();
      });
    }
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      // CustomScrollViewは、SliverAppBarやSliverList、SliverGridなどのウィジェットを組み合わせて使う
      body: CustomScrollView(
        slivers: <Widget>[
          // SliverAppBarは、AppBarのように画面上部に配置されるヘッダーを作成する
          const SliverAppBar(
            pinned: true,
            expandedHeight: 80.0,
            flexibleSpace: FlexibleSpaceBar(
              title: Text('Demo'),
            ),
          ),
          // SliverListは、ListViewのようにリストを作成する
          SliverList(
            delegate: SliverChildListDelegate([
              Container(
                height: 200.0,
                color: Colors.grey[300],
                child: Center(
                  child: Icon(
                    Icons.account_circle,
                    size: 100.0,
                    color: Colors.grey[500],
                  ),
                ),
              ),
              // ListTileは、リストのアイテムを作成する
              ListTile(
                leading: Icon(Icons.person),
                title: Text('User Name'),
              ),
              Divider(),
              ListTile(
                leading: Icon(Icons.photo),
                title: Text('Photos'),
              ),
              ListTile(
                leading: Icon(Icons.video_library),
                title: Text('Videos'),
              ),
              ListTile(
                leading: Icon(Icons.article),
                title: Text('Articles'),
              ),
              ListTile(
                leading: Icon(Icons.group),
                title: Text('Groups'),
              ),
              ListTile(
                leading: Icon(Icons.settings),
                title: Text('Settings'),
              ),
            ]),
          ),
          // SliverGridは、GridViewのようにグリッドを作成する
          SliverGrid(
            gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
              maxCrossAxisExtent: 200.0,
              mainAxisSpacing: 10.0,
              crossAxisSpacing: 10.0,
              childAspectRatio: 1.0,
            ),
            delegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                if (index < albums.length) {
                  return Container(
                    alignment: Alignment.center,
                    color: Colors.teal[100 * (index % 3)],
                    child: Text(albums[index].title),
                  );
                } else {
                  return null;
                }
              },
              childCount: albums.length,
            ),
          ),
          // SliverFixedExtentListは、ListViewのようにリストを作成する
          SliverFixedExtentList(
            itemExtent: 50.0,
            delegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                if (index < posts.length) {
                  return Container(
                    alignment: Alignment.center,
                    color: Colors.lightBlue[100 * (index % 3)],
                    child: Text(posts[index].title),
                  );
                } else {
                  return null;
                }
              },
              childCount: posts.length,
            ),
          ),
        ],
      ),
    );
  }
}

コードの解説

  • SliverAppBarは、AppBarのように、画面上部に表示されるヘッダーとして使う
SliverAppBar(
            pinned: true,
            expandedHeight: 80.0,
            flexibleSpace: FlexibleSpaceBar(
              title: Text('Demo'),
            ),
          ),
  • SliverListは、ListViewと同じように使う
SliverList(
            delegate: SliverChildListDelegate([
              Container(
                height: 200.0,
                color: Colors.grey[300],
                child: Center(
                  child: Icon(
                    Icons.account_circle,
                    size: 100.0,
                    color: Colors.grey[500],
                  ),
                ),
              ),
              // ListTileは、リストのアイテムを作成する
              ListTile(
                leading: Icon(Icons.person),
                title: Text('User Name'),
              ),
              Divider(),
              ListTile(
                leading: Icon(Icons.photo),
                title: Text('Photos'),
              ),
              ListTile(
                leading: Icon(Icons.video_library),
                title: Text('Videos'),
              ),
              ListTile(
                leading: Icon(Icons.article),
                title: Text('Articles'),
              ),
              ListTile(
                leading: Icon(Icons.group),
                title: Text('Groups'),
              ),
              ListTile(
                leading: Icon(Icons.settings),
                title: Text('Settings'),
              ),
            ]),
          ),
  • SliverGridは、GridViewのようにグリッドを作成する
SliverGrid(
            gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
              maxCrossAxisExtent: 200.0,
              mainAxisSpacing: 10.0,
              crossAxisSpacing: 10.0,
              childAspectRatio: 1.0,
            ),
            delegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                if (index < albums.length) {
                  return Container(
                    alignment: Alignment.center,
                    color: Colors.teal[100 * (index % 3)],
                    child: Text(albums[index].title),
                  );
                } else {
                  return null;
                }
              },
              childCount: albums.length,
            ),
          ),
  • SliverFixedExtentListは、ListViewのようにリストを作成する
SliverFixedExtentList(
            itemExtent: 50.0,
            delegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                if (index < posts.length) {
                  return Container(
                    alignment: Alignment.center,
                    color: Colors.lightBlue[100 * (index % 3)],
                    child: Text(posts[index].title),
                  );
                } else {
                  return null;
                }
              },
              childCount: posts.length,
            ),
          ),

🦾こんな感じのUIが作れました



まとめ

CustomScrollViewを使用すれば、ListViewやGridViewを組み合わせて、複雑なUIを作り出すことができます。投稿画像や投稿した文章を同じページで、複数表示するのに、向いています。

Discussion