PR

[Flutter]Flutter Widget of the Week「#31 Draggable」、「#32 AnimatedList」、「#33 Flexible」

Flutter

はじめに

前回はFlutter Widget of the Weekの「#28 Dismissible」、「#29 SizedBox」、「#30 ValueListenableBuilder」を紹介しました。

今回はその続きで「#31 Draggable」、「#32 AnimatedList」、「#33 Flexible」の3つです。

前回の記事はこちら

Flutter Widget of the Week

環境

  • Flutter 2.10.3

記事にした時点でのバージョンです。GitHubに公開しているのは常に最新の安定版(stable)を使う予定です。

#31 Draggable

Draggableとは

ドラッグ&ドロップを実現するためのウィジェットです。

Draggableがドラッグする対象の設定で、ドロップする場所はDragTargetウィジェットを利用します。

サンプルコード

import 'package:flutter/material.dart';

class SamplePage031 extends StatefulWidget {
  const SamplePage031({
    Key? key,
  }) : super(key: key);

  @override
  State<SamplePage031> createState() => _SamplePage031State();
}

class _SamplePage031State extends State<SamplePage031> {
  Color _selectedColor = Colors.white;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Draggable'),
        centerTitle: true,
      ),
      body: SafeArea(
        child: Column(
          mainAxisSize: MainAxisSize.max,
          children: [
            Container(
              width: double.infinity,
              height: 120,
              color: Colors.grey[100],
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: const [
                  _SamplePage031Draggable(
                    color: Colors.blue,
                  ),
                  _SamplePage031Draggable(
                    color: Colors.yellow,
                  ),
                  _SamplePage031Draggable(
                    color: Colors.red,
                  ),
                ],
              ),
            ),
            Expanded(
              child: Container(
                color: _selectedColor,
                child: Center(
                  child: DragTarget<Color>(
                    onWillAccept: (value) => value != _selectedColor,
                    onAccept: (value) {
                      setState(() {
                        _selectedColor = value;
                      });
                    },
                    builder: (context, candidates, rejects) {
                      return Container(
                        width: 100,
                        height: 100,
                        decoration: BoxDecoration(
                          border: Border.all(color: Colors.black),
                          color: Colors.white,
                        ),
                        child: const Center(
                          child: Text('ここに\nドラッグ'),
                        ),
                      );
                    },
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class _SamplePage031Draggable extends StatelessWidget {
  const _SamplePage031Draggable({
    Key? key,
    this.color,
  }) : super(key: key);

  final Color? color;

  @override
  Widget build(BuildContext context) {
    return Draggable<Color>(
      data: color,
      childWhenDragging: _SamplePage031Child(
        color: color,
        child: const Text('ドラッグ中'),
      ),
      feedback: _SamplePage031Child(
        color: color,
        child: const Icon(Icons.face),
      ),
      child: _SamplePage031Child(
        color: color,
        child: const Text('通常'),
      ),
    );
  }
}

class _SamplePage031Child extends StatelessWidget {
  const _SamplePage031Child({
    Key? key,
    this.color,
    this.child,
  }) : super(key: key);

  final Color? color;
  final Widget? child;

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 100,
      height: 100,
      color: color,
      child: Center(child: child),
    );
  }
}

結果

上の3色のいずれかをドラッグして、「ここへドラッグ」でドロップすると。。。

指定した色に変化します。

動画

公式リファレンス

Draggable class - widgets library - Dart API
API docs for the Draggable class from the widgets library, for the Dart programming language.

#32 AnimatedList

AnimatedListとは

リスト表示で追加や削除時にアニメーションをさせたい場合にこのAnimatedListを使います。

サンプルコード

import 'package:flutter/material.dart';

class SamplePage032 extends StatefulWidget {
  const SamplePage032({
    Key? key,
  }) : super(key: key);

  @override
  State<SamplePage032> createState() => _SamplePage032State();
}

class _SamplePage032State extends State<SamplePage032> {
  final _listKey = GlobalKey<AnimatedListState>();

  final _data = <String>[
    '0',
    '1',
    '2',
    '3',
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('AnimatedList'),
        centerTitle: true,
      ),
      body: SafeArea(
        child: AnimatedList(
          key: _listKey,
          initialItemCount: _data.length,
          itemBuilder: (context, index, animation) {
            return _SamplePage032Item(
              animation: animation,
              title: Text(_data[index]),
              onTap: () {
                final removeIndex = index;
                final removeItem = _data[index];

                _SamplePage032Item builder(
                  BuildContext context,
                  Animation<double> animation,
                ) {
                  return _SamplePage032Item(
                    animation: animation,
                    title: Text(removeItem),
                  );
                }

                _data.removeAt(removeIndex);
                _listKey.currentState!.removeItem(removeIndex, builder);
              },
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            final index = _data.length;
            _data.insert(index, index.toString());
            _listKey.currentState!.insertItem(index);
          });
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

class _SamplePage032Item extends StatelessWidget {
  const _SamplePage032Item({
    Key? key,
    required this.animation,
    this.title,
    this.onTap,
  }) : super(key: key);

  final Animation<double> animation;
  final Widget? title;
  final void Function()? onTap;

  @override
  Widget build(BuildContext context) {
    return SizeTransition(
      sizeFactor: animation,
      child: ListTile(
        title: title,
        onTap: onTap,
      ),
    );
  }
}

結果

分かりづらいですが、追加時にアニメーションしてます。

アイテムをタップすると、こちらも分かりづらいですがアニメーションしながら削除されます。

動画

公式リファレンス

AnimatedList class - widgets library - Dart API
API docs for the AnimatedList class from the widgets library, for the Dart programming language.

#33 Flexible

Flexibleとは

親ウィジェットのサイズに合わせて、相対的に大きさを変更したい場合にこのウィジェットを使います。

サンプルコード

import 'package:flutter/material.dart';

class SamplePage033 extends StatefulWidget {
  const SamplePage033({
    Key? key,
  }) : super(key: key);

  @override
  State<SamplePage033> createState() => _SamplePage033State();
}

class _SamplePage033State extends State<SamplePage033> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flexible'),
        centerTitle: true,
      ),
      body: SafeArea(
        child: Column(
          children: [
            Flexible(
              flex: 2,
              child: Container(
                color: Colors.cyan,
                child: const Center(
                  child: Text(
                    '1/3\n(2 Flex / 6 Total)',
                    textAlign: TextAlign.center,
                    style: TextStyle(color: Colors.white),
                  ),
                ),
              ),
            ),
            Flexible(
              flex: 3,
              child: Container(
                color: Colors.teal,
                child: const Center(
                  child: Text(
                    '1/2\n(3 Flex / 6 Total)',
                    textAlign: TextAlign.center,
                    style: TextStyle(color: Colors.white),
                  ),
                ),
              ),
            ),
            Flexible(
              flex: 1,
              child: Container(
                color: Colors.indigo,
                child: const Center(
                  child: Text(
                    '1/6\n(1 Flex / 6 Total)',
                    textAlign: TextAlign.center,
                    style: TextStyle(color: Colors.white),
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

結果

それぞれ、画面に対して割合になっています。仮に解像度が変わってもこの割合はかわりません。

動画

公式リファレンス

Flexible class - widgets library - Dart API
API docs for the Flexible class from the widgets library, for the Dart programming language.

さいごに

DraggableFlexibleは頻繁に使いそうなのでしっかり覚えておきたいですね。

AnimatedListはワタシ的にはそこまで必要じゃなさそうですね。

おすすめ参考書

リンク

GitHub - nobushiueshi/flutter_widget_of_the_week
Contribute to nobushiueshi/flutter_widget_of_the_week development by creating an account on GitHub.

コメント

タイトルとURLをコピーしました