2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Flutter/Riverpod でスライダー作成

Last updated at Posted at 2021-12-26

環境

  • MaxOS Monterey
  • Flutter 2.8.1
  • Dart 2.15.1

Flutter&Riverpod でスライダーを実装しました。
より良いコーディングやアーキテクチャなどあればコメント欄にお願いいたします!

完成形

スライド.gif

フォルダ構成

flutter_app/
├ images/
│ └ image.jpg
├ lib/
│ ├ models/
│ │  └ image_slider.dart
│ ├ providers/
│ │  └ image_sliders.dart
│ ├ screens/
│ │  └ screen.dart
│ └ main.dart
└ pubspec.yaml

実装

package

最新バージョンなど詳細は公式でご確認ください
carousel_slider
hooks_riverpod
flutter_hooks

pubspec.yaml
carousel_slider: ^4.0.0
hooks_riverpod: ^1.0.3
flutter_hooks: ^0.18.1

Main

  • Riverpod を使用するため main.darthooks_riverpod
    をインポート
  • MyApp()ProviderScope() で囲う
main.dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; // 追加
import './screens/slider.dart';

void main() {
  runApp(
    const ProviderScope( // 追加
      child: MyApp(),
    )
  );
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: SliderScreen(),
    );
  }
}

Model

image_slider.dart
import 'package:flutter/cupertino.dart';

class ImageSlider {
  final Image image;

  ImageSlider({ required this.image });
}

Provider

ドットをクリックした時に、画像を変更したいので
carousel_controller のインポートとコントローラを定義

image_sliders.dart
import 'dart:collection'; // UnmodifiableListView の為
import 'package:flutter/material.dart';
import 'package:carousel_slider/carousel_controller.dart'; // 追加
import '../models/image_slider.dart';

class ImageSliders extends ChangeNotifier {
  // お好きな画像を定義
  final List<ImageSlider> _imageSliders = [
    ImageSlider(image: Image.asset('images/dog1.jpg')),
    ImageSlider(image: Image.asset('images/dog2.jpg')),
    ImageSlider(image: Image.asset('images/dog3.jpg')),
    ImageSlider(image: Image.asset('images/dog4.jpg')),
    ImageSlider(image: Image.asset('images/dog5.jpg')),
  ];

  int _currentPoint = 0;

  // 追加
  CarouselController buttonCarouselController = CarouselController();

  // 他ファイルで _imageSliders に対して、add などの処理を防ぐ?
  UnmodifiableListView<ImageSlider> get imageSliders {
    return UnmodifiableListView(_imageSliders);
  }

  int get currentPoint {
    return _currentPoint;
  }

  void setCurrentPoint(int index) {
    _currentPoint = index;
    notifyListeners(); // 変更を通知
  }
}

View

Riverpod を実装

  • hooks_riverpod をインポート
  • プロバイダーを定義
  • extends を HookConsumerWidget に変更
  • Widget build() の引数に、WidgetRef ref を追加

スライダーを実装

  • ref.read(imageSliderProvider).imageSliders で定義した画像を呼び出し
  • CarouselSlider(~) でスライダー実装(詳しくは公式参照)

ref.watch, ref.read があるが、
変更を検知して表示を変更したいなら、watch (Dotsで使用)
呼び出すだけで良いなら、read
で使い分けるっぽい

slider.dart
import 'package:flutter/material.dart';
import 'package:carousel_slider/carousel_slider.dart'; // 追加
import 'package:hooks_riverpod/hooks_riverpod.dart'; // 追加
import '../models/image_slider.dart';
import '../providers/image_sliders.dart';

// プロバイダー定義
final imageSliderProvider = ChangeNotifierProvider((ref) => ImageSliders());

class SliderScreen extends HookConsumerWidget { // 変更
  const SliderScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) { // 追加
    // 画像の読み込み
    final List<ImageSlider> _imageSliders = ref.read(imageSliderProvider).imageSliders;

    return Scaffold(
      appBar: AppBar(
        title: const Text('Dog Slider'),
      ),
      body: Padding(
        padding: const EdgeInsets.symmetric(vertical: 30.0),
        child: Column(
          children: [
            CarouselSlider( // スライダー
              carouselController: ref.read(imageSliderProvider).buttonCarouselController, // DotsIndicator用
              options: CarouselOptions(
                enlargeCenterPage: true, // 真ん中の画像以外を少し小さく表示
                height: MediaQuery.of(context).size.height / 4, // 高さ(お好みで)
                viewportFraction: 0.75, // 画像の横幅
                autoPlay: true, // 自動スクロール
                autoPlayInterval: const Duration(seconds: 5), // 自動スクロールの間隔
                onPageChanged: (index, _) {
                  // 現在の画像の位置を設定
                  ref.read(imageSliderProvider).setCurrentPoint(index);
                },
              ),
              items: _imageSliders.map((imageSlider) {
                return Builder(
                  builder: (BuildContext context) {
                    return SizedBox(
                      width: MediaQuery.of(context).size.width,
                      child: imageSlider.image,
                    );
                  },
                );
              }).toList(),
            ),
            DotsIndicator(imageSliders: _imageSliders),
          ],
        ),
      ),
    );
  }
}
~~
続く
~~

ドットインディケーター

  • DotsをWidget で分けているが、どちらでも
  • ref.watch(topSliderProvider).currentPoint で変更を検知し、カラーを変更
  • .buttonCarouselController.animateToPage(index)でタップした箇所の画像へスライド
slider.dart
~~
続き
~~
// ドット部分
class DotsIndicator extends HookConsumerWidget {
  const DotsIndicator({
    Key? key,
    required List<TopSlider> topSliders,
  }) : _topSliders = topSliders, super(key: key);

  final List<TopSlider> _topSliders;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: _topSliders.map((topSlider) {
        int index = _topSliders.indexOf(topSlider);
        return GestureDetector(
          child: Container(
            width: 8.0,
            height: 8.0,
            margin: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 2.0),
            decoration: BoxDecoration(
              shape: BoxShape.circle,
              color: ref.watch(topSliderProvider).currentPoint == index ?
                  const Color.fromRGBO(0, 0, 0, 0.9) : const Color.fromRGBO(0, 0, 0, 0.4),
            ),
          ),
          onTap: () => ref.read(topSliderProvider).buttonCarouselController.animateToPage(index),
        );
      }).toList(),
    );
  }
}

これで動くはず!

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?