🍏

Cupertino widgets④

に公開

長押しすると表示されるモーダル

はい。タイトルの通り長押しすると表示されるモーダルをCupertinoContextMenu classで作ることができます。こちらはサンプルコードがあったので、サンプルコードがなかったCupertinoContextMenuAction classを使ってみようと思います。

Use CupertinoContextMenu

CupertinoContextMenuを使用して、iOS風の長押しメニューを実装するデモを作成します。写真や投稿に対するアクションメニューを例として実装してみましょう。

https://youtube.com/shorts/kV93QH0cCQM

example
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';

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

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

  
  Widget build(BuildContext context) {
    return const CupertinoApp(
      debugShowCheckedModeBanner: false,
      theme: CupertinoThemeData(brightness: Brightness.light),
      home: ContextMenuDemo(),
    );
  }
}

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

  
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      navigationBar: const CupertinoNavigationBar(
        middle: Text('コンテキストメニューデモ'),
      ),
      child: SafeArea(
        child: ListView(
          padding: const EdgeInsets.all(16),
          children: [
            // テキストのコンテキストメニュー
            const Text(
              'テキストを長押ししてみてください',
              textAlign: TextAlign.center,
            ),
            const SizedBox(height: 16),
            CupertinoContextMenu(
              actions: <CupertinoContextMenuAction>[
                CupertinoContextMenuAction(
                  onPressed: () {
                    Clipboard.setData(const ClipboardData(
                      text: 'これはサンプルテキストです。長押しでコピーできます。',
                    ));
                    Navigator.pop(context);
                  },
                  trailingIcon: CupertinoIcons.doc_on_doc,
                  child: const Text('コピー'),
                ),
                CupertinoContextMenuAction(
                  onPressed: () {
                    Navigator.pop(context);
                  },
                  trailingIcon: CupertinoIcons.share,
                  child: const Text('共有'),
                ),
              ],
              child: Container(
                padding: const EdgeInsets.all(16),
                decoration: BoxDecoration(
                  color: CupertinoColors.systemGrey6,
                  borderRadius: BorderRadius.circular(8),
                ),
                child: const Text(
                  'これはサンプルテキストです。長押しでコピーできます。',
                  style: TextStyle(fontSize: 16),
                ),
              ),
            ),
            const SizedBox(height: 32),

            // カスタムカードのコンテキストメニュー
            const Text(
              'カードを長押ししてみてください',
              textAlign: TextAlign.center,
            ),
            const SizedBox(height: 16),
            CupertinoContextMenu(
              actions: <CupertinoContextMenuAction>[
                CupertinoContextMenuAction(
                  onPressed: () {
                    Navigator.pop(context);
                  },
                  trailingIcon: CupertinoIcons.pencil,
                  child: const Text('編集'),
                ),
                CupertinoContextMenuAction(
                  onPressed: () {
                    Navigator.pop(context);
                  },
                  trailingIcon: CupertinoIcons.bookmark,
                  child: const Text('ブックマーク'),
                ),
                CupertinoContextMenuAction(
                  onPressed: () {
                    Navigator.pop(context);
                  },
                  trailingIcon: CupertinoIcons.share,
                  child: const Text('共有'),
                ),
                CupertinoContextMenuAction(
                  isDestructiveAction: true,
                  onPressed: () {
                    Navigator.pop(context);
                  },
                  trailingIcon: CupertinoIcons.delete,
                  child: const Text('削除'),
                ),
              ],
              child: Container(
                padding: const EdgeInsets.all(16),
                decoration: BoxDecoration(
                  color: CupertinoColors.systemGrey6,
                  borderRadius: BorderRadius.circular(8),
                  border: Border.all(
                    color: CupertinoColors.systemGrey4,
                    width: 1,
                  ),
                ),
                child: const Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Text(
                      'カスタムカード',
                      style: TextStyle(
                        fontSize: 18,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    SizedBox(height: 8),
                    Text(
                      'これはカスタムカードのサンプルです。長押しで様々なアクションを実行できます。',
                      style: TextStyle(fontSize: 14),
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

このデモでは以下の2つのコンテキストメニューの例を実装しています:

  1. テキストのコンテキストメニュー
  • コピー(クリップボードにコピー)
  • 共有
  1. カスタムカードのコンテキストメニュー
  • 編集
  • ブックマーク
  • 共有
  • 削除(破壊的アクション)

主な特徴:

  1. アクションの種類
CupertinoContextMenuAction(
  onPressed: () { /* アクション */ },
  trailingIcon: CupertinoIcons.heart,  // アイコンの追加
  child: const Text('いいね'),
)
  1. 破壊的アクション
CupertinoContextMenuAction(
  isDestructiveAction: true,  // 赤色で表示
  onPressed: () { /* アクション */ },
  child: const Text('削除'),
)
  1. 実用的な機能
// クリップボードへのコピー
Clipboard.setData(const ClipboardData(text: 'テキスト'));
  1. カスタマイズ可能なデザイン
Container(
  decoration: BoxDecoration(
    borderRadius: BorderRadius.circular(8),
    // その他のスタイリング
  ),
  child: /* コンテンツ */,
)

使用シーン:

  1. SNSアプリ
  • 投稿の操作
  • コメントの操作
  • プロフィール情報の操作
  1. メッセージアプリ
  • メッセージの操作
  • 添付ファイルの操作
  • 連絡先の操作
  1. ドキュメントアプリ
  • テキストの編集操作
  • ファイルの操作
  • フォルダの操作

注意点:

  1. メニューが開いている時は必ず閉じる方法を提供
onPressed: () {
  Navigator.pop(context);  // メニューを閉じる
}
  1. 適切なアイコンの選択
  2. 破壊的アクションの慎重な使用
  3. アクセシビリティへの配慮

このように、CupertinoContextMenuはiOSスタイルのアプリで直感的な操作を提供するのに役立ちます。

Discussion