🧠

Flutterでスライドの資料を作ってみた(flutter_deck)

2024/07/29に公開

はじめに

この記事は私の作業履歴となっているため、まとまったものが読みたい方はこちらを参照してください。

未執筆

環境

  • OS:MacOS Sonoma 14.5
  • Flutter 3.19.4
  • Dart 3.3.2

使用するパッケージ

https://pub.dev/packages/flutter_deck

flutter pub add flutter_deck:^0.14.0

私の環境だと^0.15.0は起動時にエラーが発生しました。

追加で入れたパッケージ
  • build_runner
  • flutter_gen_runner

導入

main.dartは一旦はこんな感じになりました。
とりあえず背景色とspeakInfoslideSize設定してみました。slidesにはスライドのウィジェットを入れるぽいです。

main.dart
import 'package:flutter/material.dart';
import 'package:flutter_deck/flutter_deck.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return FlutterDeckApp(
      speakerInfo: FlutterDeckSpeakerInfo(
        name: 'Hibana',
        description: 'エンジニア',
        socialHandle: '@r5437198',
        imagePath: Assets.me.path,
      ),
      configuration: FlutterDeckConfiguration(
        background: const FlutterDeckBackgroundConfiguration(
          light: FlutterDeckBackground.solid(
            Color(0xFFFFFFFF),
          ),
          dark: FlutterDeckBackground.solid(
            Color(0xFF192638),
          ),
        ),
        slideSize: FlutterDeckSlideSize.fromAspectRatio(
          aspectRatio: const FlutterDeckAspectRatio.ratio16x10(),
        ),
      ),
      slides: [],
    );
  }
}

つくってみる

今回はゴリラについてのスライドを作りたいと思います。ゴリラの理由は特にないです。

ざっくり作る内容です。(所々、スライドの作成工程は省略します)

  • タイトル
  • 目次
  • なぜゴリラ?
  • ゴリラについて
  • ゴリラを取り巻く問題
  • まとめ
タイトルスライド(FlutterDeckSlide.blank)

スライドにはまずタイトルのスライドが必要ですね。
基本的にFlutterDeckはFlutterDeckSlideWidgetを継承して、記載するみたいです。

title.dart
import 'package:flutter/material.dart';
import 'package:flutter_deck/flutter_deck.dart';

class TitleSlide extends FlutterDeckSlideWidget {
  TitleSlide()
      : super(
          configuration: const FlutterDeckSlideConfiguration(
            route: '/title',
            title: 'ゴリラについて',
            footer: FlutterDeckFooterConfiguration(
              showFooter: false,
            ),
          ),
        );

  
  FlutterDeckSlide build(BuildContext context) {
    return FlutterDeckSlide.title(
      title: 'ゴリラについて',
    );
  }
}

文字が小さいですね。カスタムしてタイトルスライドを作りたい場合はFlutterDeckSlide.blankを使ったほうがいいみたいです。

title.dart
-    return FlutterDeckSlide.title(
-      title: 'ゴリラについて',
-    );
+    return FlutterDeckSlide.blank(
+      builder: (context) => Padding(
+        padding: const EdgeInsets.symmetric(
+          horizontal: 120,
+        ),
+        child: Row(
+          children: [
+            Column(
+              mainAxisSize: MainAxisSize.min,
+              crossAxisAlignment: CrossAxisAlignment.start,
+              children: [
+                const Text(
+                  'ゴリラについて',
+                  style: TextStyle(
+                    fontSize: 88,
+                  ),
+                ),
+                FlutterDeckSpeakerInfoWidget(
+                  speakerInfo: FlutterDeckSpeakerInfo(
+                    name: 'Hibana',
+                    description: 'エンジニア',
+                    socialHandle: '@r5437198',
+                    imagePath: Assets.me.path,
+                  ),
+                )
+              ],
+            ),
+          ],
+        ),
+      ),
+    );


今度は逆に文字が大きいかも、まあいいか。

目次スライド(FlutterDeckSlide.template)

目次はこんな感じになりました。

import 'package:flutter/material.dart';
import 'package:flutter_deck/flutter_deck.dart';

class TemplateSlide extends FlutterDeckSlideWidget {
  TemplateSlide({
    required this.route,
    required this.title,
    this.steps = 1,
    required this.child,
  }) : super(
          configuration: FlutterDeckSlideConfiguration(
            route: route,
            steps: steps,
            header: FlutterDeckHeaderConfiguration(
              title: title,
            ),
          ),
        );

  final String route;
  final String title;
  final int steps;
  final Widget child;

  
  FlutterDeckSlide build(BuildContext context) {
    return FlutterDeckSlide.template(
      headerBuilder: (context) => FlutterDeckHeader(title: title),
      contentBuilder: (context) => Padding(
        padding: const EdgeInsets.symmetric(
          // NOTE: FlutterDeckSlide.blankのデフォルトサイドパディング16と合わせた
          horizontal: 16 + 120,
          vertical: 60,
        ),
        child: child,
      ),
    );
  }
}
import 'package:flutter/cupertino.dart';
import 'package:try_flutter_deck/slides/template.dart';

class IndexSlide extends TemplateSlide {
  IndexSlide()
      : super(
          route: '/index',
          title: '目次',
          child: const Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Padding(
                padding: EdgeInsets.symmetric(
                  vertical: 60,
                ),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      '・なぜゴリラ?',
                      style: TextStyle(
                        fontSize: 96,
                      ),
                    ),
                    Text(
                      '・ゴリラについて',
                      style: TextStyle(
                        fontSize: 96,
                      ),
                    ),
                    Text(
                      '・ゴリラを取り巻く問題',
                      style: TextStyle(
                        fontSize: 96,
                      ),
                    ),
                    Text(
                      '・まとめ',
                      style: TextStyle(
                        fontSize: 96,
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
        );
}


目次書きながら最近人前で話してないなーと思ったり

目的スライド(FlutterDeckSlideStepsBuilder)

FlutterDeckSlideConfigurationstepsに記載した回数、FlutterDeckSlideStepsBuilderで変化させることができます。

ページ送りすると、URLは以下のように変わります。

http://localhost/#/purpose?step=2 -> http://localhost/#/purpose?step=3

コードで見た方が分かりやすいと思います。

import 'package:flutter/material.dart';
import 'package:flutter_deck/flutter_deck.dart';
import 'package:try_flutter_deck/slides/template.dart';

class PurposeSlide extends TemplateSlide {
  PurposeSlide()
      : super(
          route: '/purpose',
          title: '目的',
          steps: 3,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Padding(
                padding: const EdgeInsets.symmetric(
                  vertical: 60,
                ),
                child: FlutterDeckSlideStepsBuilder(
                  builder: (BuildContext _, int step) => Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: _getTextList().getRange(0, step).toList(),
                  ),
                ),
              ),
            ],
          ),
        );

  static List<Widget> _getTextList() => const [
        Center(
          child: Text(
            '皆さん、しりとりで必ず「ゴリラ」使いますよね?',
            style: TextStyle(
              fontSize: 80,
            ),
          ),
        ),
        Center(
          child: Padding(
            padding: EdgeInsets.symmetric(
              vertical: 72,
            ),
            child: Icon(
              Icons.arrow_downward,
              size: 96,
            ),
          ),
        ),
        Center(
          child: Text(
            'ゴリラについて詳しくなりましょう!',
            style: TextStyle(
              fontSize: 80,
              color: Colors.amber,
            ),
          ),
        ),
      ];
}


初期表示だと 皆さん、しりとりで必ず「ゴリラ」使いますよね? のみ表示されています。ページ送りするごとに矢印ゴリラについて詳しくなりましょう! が表示されます。

ゴリラについてスライド(FlutterDeckSlide.split)
import 'package:flutter/material.dart';
import 'package:flutter_deck/flutter_deck.dart';
import 'package:try_flutter_deck/gen/assets.gen.dart';

class AboutGorillaSlide extends FlutterDeckSlideWidget {
  AboutGorillaSlide()
      : super(
          configuration: const FlutterDeckSlideConfiguration(
            route: '/about-gorilla',
            header: FlutterDeckHeaderConfiguration(
              title: 'ゴリラについて',
            ),
          ),
        );

  
  FlutterDeckSlide build(BuildContext context) {
    return FlutterDeckSlide.split(
      theme: FlutterDeckTheme.of(context).copyWith(
        splitSlideTheme: const FlutterDeckSplitSlideThemeData(
          rightBackgroundColor: Color(0xFF192638),
          leftBackgroundColor: Color(0xFF192638),
        ),
      ),
      leftBuilder: (context) => const Padding(
        padding: EdgeInsets.only(
          left: 120,
          top: 120,
        ),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              '日本の動物園にいるのは全てこのゴリラ',
              style: TextStyle(
                fontSize: 64,
                color: Colors.amber,
              ),
            ),
            Divider(height: 96, thickness: 4),
            Text(
              '生息地:中央アフリカ、カメルーンなど',
              style: TextStyle(
                fontSize: 64,
              ),
            ),
            Text(
              '体重:100〜200kg',
              style: TextStyle(
                fontSize: 64,
              ),
            ),
            Text(
              '性格:繊細、ストレスで体調を崩しやすい',
              style: TextStyle(
                fontSize: 64,
              ),
            ),
          ],
        ),
      ),
      rightBuilder: (context) => Column(
        children: [
          const SizedBox(height: 120 + 16),
          Assets.gorilla.image(),
          const SizedBox(height: 8),
          const Text(
            'ニシローランドゴリラ',
            style: TextStyle(
              fontSize: 56,
            ),
          ),
        ],
      ),
    );
  }
}


再配布可能なゴリラのフリー画像が見つけられなかったため、ダミー画像になっています。

ゴリラについてのスライド2(FlutterDeckSlide.bigFact)
import 'package:flutter/material.dart';
import 'package:flutter_deck/flutter_deck.dart';

class AboutGorillaNameSlide extends FlutterDeckSlideWidget {
  const AboutGorillaNameSlide()
      : super(
          configuration: const FlutterDeckSlideConfiguration(
            route: '/about-gorilla/name',
            header: FlutterDeckHeaderConfiguration(
              title: 'ゴリラについて',
            ),
          ),
        );

  
  FlutterDeckSlide build(BuildContext context) {
    return FlutterDeckSlide.bigFact(
      title: 'ゴリラ=ゴリラ=ゴリラ',
      subtitle: 'ゴリラ科ゴリラ属ゴリラ種という意味',
      theme: FlutterDeckTheme.of(context).copyWith(
        bigFactSlideTheme: const FlutterDeckBigFactSlideThemeData(
          titleTextStyle: TextStyle(color: Colors.amber),
        ),
      ),
    );
  }
}

完成した全てのスライド

スライド









感想

スライドをコードで保管したい人やFlutterに慣れている人はFlutterDeckを使用してみてもいいんじゃないかと思いました。また、これが一番強みであると思ってて(今回は使用してないですが)、Flutterなのでボタン等もスライド上でちゃんと動きます。FirebaseRemoteConfigを使用すれば、アプリケーションごとスライドに埋め込むことができるぽいです。
https://github.com/mkobuolys/f3-firebase-remote-config-talk

今回のコード

https://github.com/R5437198/try-flutter-deck

参考

Discussion