テクノロジー

FlutterではこんなUIちょちょいのちょい

はじめに

Flutterでは「Widget(ウィジェット)」と呼ばれる部品を組み合わせて、アプリの画面を構築します。HTMLでいえば、div や h1 といったタグのような役割を担っています。
この記事では、Flutterにあらかじめ用意されている一部の標準のWidgetに絞り、それらを使って手軽にどのようなUIが構築できるのかを、最小限のコードと併せてご紹介します。

動作環境

以下の環境で動作確認を行いました。

$ flutter --version
Flutter 3.27.3 • channel stable • https://github.com/flutter/flutter.git
Framework • revision c519ee916e (7 months ago)2025-01-21 10:32:23 -0800
Engine • revision e672b006cb
ToolsDart 3.6.1DevTools 2.40.2

目次

ドラッグで入れ替え可能なリスト(ReorderableListView)

リスト項目をドラッグ操作で並べ替えられるWidgetです。TODO管理や並び順変更機能に最適です。

import 'package:flutter/material.dart';

void main() => runApp(const MaterialApp(home: MyList()));

class MyList extends StatefulWidget {
  const MyList({super.key});

  @override
  State<MyList> createState() => _MyListState();
}

class _MyListState extends State<MyList> {
  final items = ['りんご', 'ごりら', 'ラッパ', 'パイナップル'];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('asial')),
      body: ReorderableListView(
        onReorder: (oldIndex, newIndex) {
          setState(() {
            if (newIndex > oldIndex) newIndex--;
            final item = items.removeAt(oldIndex);
            items.insert(newIndex, item);
          });
        },
        children: List.generate(items.length, (index) {
          return ListTile(
            key: ValueKey(items[index]),
            title: Text(items[index]),
            trailing: ReorderableDragStartListener(
              index: index,
              child: const Icon(Icons.drag_handle),
            ),
          );
        }),
      ),
    );
  }
}

タブでの画面切り替え(DefaultTabController、TabBar、TabBarView)

タブをタップやスワイプして画面を切り替えられるWidgetの組み合わせです。カテゴリ別画面やコンテンツ切り替えに便利です。

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: DefaultTabController(
        length: 3,
        child: Scaffold(
          appBar: AppBar(
            title: const Text('アシアル'),
            bottom: const TabBar(
              indicatorSize: TabBarIndicatorSize.tab,
              tabs: [
                Tab(text: 'タブ1'),
                Tab(text: 'タブ2'),
                Tab(text: 'タブ3'),
              ],
            ),
          ),
          body: const TabBarView(
            children: [
              Center(child: Text('タブ1の内容')),
              Center(child: Text('タブ2の内容')),
              Center(child: Text('タブ3の内容')),
            ],
          ),
        ),
      ),
    );
  }
}

複数のステップを順番に案内する(Stepper)

複数の手順を段階的に進められるUIを提供します。フォーム入力やチュートリアルの案内に向いています。

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

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

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

class StepperExample extends StatefulWidget {
  const StepperExample({super.key});

  @override
  State<StepperExample> createState() => _StepperExampleState();
}

class _StepperExampleState extends State<StepperExample> {
  int _currentStep = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('アシアル')),
      body: Stepper(
        currentStep: _currentStep,
        onStepContinue: () {
          if (_currentStep < 2) {
            setState(() => _currentStep += 1);
          }
        },
        onStepCancel: () {
          if (_currentStep > 0) {
            setState(() => _currentStep -= 1);
          }
        },
        steps: const [
          Step(
            title: Text('ステップ 1'),
            content: Text('ここは最初のステップです。'),
          ),
          Step(
            title: Text('ステップ 2'),
            content: Text('ここは2番目のステップです。'),
          ),
          Step(
            title: Text('ステップ 3'),
            content: Text('ここは最後のステップです。'),
          ),
        ],
      ),
    );
  }
}

リストの展開(ExpansionTile)

タップで項目を展開・折りたたみできるリストUIです。FAQや詳細情報の表示に最適です。

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('アシアル')),
        body: ListView(
          children: const [
            ExpansionTile(
              title: Text('タイトル1'),
              children: [
                ListTile(title: Text('展開された内容1-1')),
                ListTile(title: Text('展開された内容1-2')),
              ],
            ),
            ExpansionTile(
              title: Text('タイトル2'),
              children: [
                ListTile(title: Text('展開された内容2-1')),
                ListTile(title: Text('展開された内容2-2')),
              ],
            ),
            ExpansionTile(
              title: Text('タイトル3'),
              children: [
                ListTile(title: Text('展開された内容3-1')),
                ListTile(title: Text('展開された内容3-2')),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

Pull to Refresh(RefreshIndicator)

リストを下に引っ張って最新データを取得できるUIです。SNSやニュースアプリの更新機能に広く使われています。

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

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

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

class RefreshExample extends StatefulWidget {
  const RefreshExample({super.key});

  @override
  State<RefreshExample> createState() => _RefreshExampleState();
}

class _RefreshExampleState extends State<RefreshExample> {
  final List<String> items = List.generate(5, (i) => 'アイテム ${i + 1}');

  Future<void> _onRefresh() async {
    await Future.delayed(const Duration(seconds: 2));
    setState(() {
      items.add('追加アイテム ${items.length + 1}');
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('アシアル')),
      body: RefreshIndicator(
        onRefresh: _onRefresh,
        child: ListView.builder(
          itemCount: items.length,
          itemBuilder: (context, index) => ListTile(
            title: Text(items[index]),
          ),
        ),
      ),
    );
  }
}

まとめ

Flutter には、今回紹介したような便利な標準Widgetが数多く揃っており、外部パッケージを使わなくても高度な UI を短いコードで実装できます。もしまだ Flutter を触ったことがないなら、まずは今回のWidgetを試してみてください。

author img for あおりんご
あおりんご

アシアルではエンジニアとしてモバイルアプリ、フロントエンド、バックエンドの開発に携わってきた。以前在籍していたSIer企業ではプロジェクトの進行・調整を主に行なっていたが、プログラミングでものをつくることによって得られる快楽を、より沢山味わうためにアシアルに転職。栄養バランスの取れた食事、深い睡眠、適度な運動を心がけ、仕事には最高のコンディションで臨む。

記事一覧

前の記事へ

一覧へ戻る

「テクノロジー」カテゴリの最新記事

PAGE TOP