【Flutter】動的なDrawer

  • 2024年7月19日
  • 2024年7月19日
  • 小物

対象者

  • Flutterを使用してモバイルアプリを開発している開発者
  • ユーザーエクスペリエンスの向上を目指しているエンジニア
  • 動的なUIコンポーネントの実装方法を学びたいプログラマー

はじめに

アプリケーションのユーザーエクスペリエンスを向上させるためには、動的なDrawerの実装が有効です。
静的なドロワーでは、あらかじめ決められた内容しか表示できず、ユーザーの操作や状態に応じたカスタマイズができません。しかし、動的なドロワーを使用することで、ユーザーのアクションに応じて表示内容を変更し、より直感的で使いやすいインターフェースを提供することができます。
例えば、Eコマースアプリでは、商品一覧で商品のサムネイルをタップしたとき、商品の詳細をドロワーとして表示することなどが可能になります。(もちろん、パッケージを使っても実現でもできます)

実装したサンプルが見つからなかったのでできるか不安でしたが、実装できましたので、まとめました。

動的なドロワーの実装方法

カスタムのDrawerクラスの作成

動的なドロワーを実現するためには、カスタムのDrawerクラス(DynamicDrawer)を作成します。このクラスは、ドロワーの内容を動的に変更します。

以下に、DynamicDrawerクラスの実装を示します。このクラスは、initCallbackというコールバック関数を受け取り、その関数の結果を表示します。

class DynamicDrawer extends StatelessWidget {
  const DynamicDrawer({required this.initCallback, super.key});

  final int Function() initCallback;

  @override
  Widget build(BuildContext context) {
    return Drawer(
      child: ListView(
        children: <Widget>[
          ListTile(
            title: Text('Item ${initCallback()}'),
            onTap: () => Navigator.pop(context),
          ),
        ],
      ),
    );
  }
}

この実装では、initCallback関数が呼び出され、その戻り値がドロワーの表示内容として使用されます。これにより、ドロワーを開くたびに関数に応じた最新の状態に応じた内容を表示することが可能になります。つまり、ドロワーに表示する内容を、Scaffoled作成時ではなく、ドロワー表示時に遅延させてます。

Callbackを用いた動的な表示

動的な表示を実現するためには、コールバック関数を適切に設定し、ボタン押下時などのタイミングでドロワーの内容を更新する必要があります。

以下に、ボタン押下時にドロワーの表示内容を変更する実装例を示します。この例では、ボタンを押すことで_selectedIndexが更新され、その値がドロワーの内容に反映されます。

<div class="joplin-editable"><pre class="joplin-source" data-joplin-language="dart" data-joplin-source-open="```dart
" data-joplin-source-close="
```">class _MyHomePageState extends State<MyHomePage> {
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
  int _selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      drawer: DynamicDrawer(initCallback: _initDrawer),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: ListView.builder(
          itemCount: 10,
          itemBuilder: (item, index) => FilledButton(
            onPressed: () {
              setState(() {
                _selectedIndex = index;
              });
              _scaffoldKey.currentState?.openDrawer();
            },
            child: Text('$index'),
          ),
        ),
      ),
    );
  }

  int _initDrawer() {
    return _selectedIndex;
  }
}

このコードでは、ボタンを押すと_selectedIndexが更新され、その後ドロワーが開かれます。DynamicDrawerクラス内のinitCallbackが呼び出され、_selectedIndexの値がドロワーの表示内容として使用されます。これにより、ユーザーの操作に応じてドロワーの内容が動的に変更されます。

以上のように、動的なドロワーを実装することで、ユーザー体験の向上や開発の柔軟性を高めることができます。動的なドロワーの必要性と利点、DynamicDrawerクラスの作成方法、コールバックを用いた動的な表示について理解することで、より効果的なアプリケーションを構築することができるでしょう。

今回はサンプルと言うことで数値を渡しましたが、本来であればCardに付随したクラスなどを渡すと、色々なことがDrawerに表示できます。

まとめ

動的なドロワーを実装することで、ユーザーの操作に応じた柔軟なUIを提供できます。今回の実装方法を参考に、自分のアプリケーションにも取り入れてみてください。

参考

ソース(main.dartにコピペして動作確認用)

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(),
      home: const MyHomePage(title: 'Flutter Dynamic Drawer'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
  int _selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      appBar: AppBar(
        title: Text(widget.title),
        automaticallyImplyLeading: false,
      ),
      drawer: DynamicDrawer(initCallback: _initDrawer),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: ListView.builder(
          itemCount: 10,
          itemBuilder: (item, index) => FilledButton(
            onPressed: () {
              _selectedIndex = index;
              _scaffoldKey.currentState?.openDrawer();
            },
            child: Text('$index'),
          ),
        ),
      ),
    );
  }

  int _initDrawer() {
    return _selectedIndex;
  }
}

class DynamicDrawer extends StatelessWidget {
  const DynamicDrawer({required this.initCallback, super.key});

  final int Function() initCallback;

  @override
  Widget build(BuildContext context) {
    return Drawer(
      child: ListView(
        children: <Widget>[
          ListTile(
            title: Text('Item ${initCallback()}'),
            onTap: () => Navigator.pop(context),
          ),
        ],
      ),
    );
  }
}