Chapter 14

  main.dartの中身

heyhey1028
heyhey1028
2023.02.17に更新

さて抽象的な説明が続きましたが、この章ではサンプルアプリの中身をより詳しく見てみましょう。

main.dart

main.dartは Flutter アプリのエントリーポイントとなるファイルです。flutter runコマンドで実際に実行しているファイルがこちらのファイルです。

Flutter でアプリを開発していくにあたって重要な要素が詰まっているので1つ1つ見ていきましょう。

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

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

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

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

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

import

import [インポートするファイルのパス] でファイル内で使用するパッケージやファイルを読み込んでいます。

import 'package:flutter/material.dart';

上記の material パッケージマテリアルデザインの UI コンポーネントを使う為のパッケージで、UI を構成するファイルでは必ずと言って良いほどインポートすることになります。

void main()

ファイルを指定して実行すると呼び出されるのが main関数です。

flutter runコマンドでは指定がなければ自動的にlib/main.dartが実行され、下記の main 関数が呼び出されます。

flutter run -t [ファイル名]とすることで実行するファイルを指定することも出来、その場合はそのファイルの main 関数が呼び出されます。

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

runApp()

runApp関数はアプリを構成する Widget 群を受け取り、描画エンジンに繋げる役割を担います。この 関数に渡される Widget がアプリのルート(根っこ) になります。

  runApp(const MyApp());

MyAppクラス

前述したStatelessWidgetを継承するクラスです。StatelessWidgetの説明は前項でしたとして、ここでは中身についていくつかポイントとなる部分を見ていきましょう。

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

MaterialApp

前述のrunAppに渡され今回のアプリの出発点となっている Widget です。

このクラスは Google が提供するマテリアルデザインに準拠する Widget とそれらで使う様々な機能を提供してくれる Widget です。

Apple が提供する Cupertino デザインに準拠する Widget と機能を提供するCupertinoAppもあります。よほど Cupertino デザインに寄せたいなどでなければ、基本MaterialAppで構いません。

アプリ全体のカラーテーマやアプリのルーティングなど様々な設定を定義することができます。

homeフィールド には最初に表示する Widget を指定します。今回はMyHomePageを指定しているので、アプリを立ち上げるとMyHomePageが表示されています。

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }

ThemeData

アプリ全体のビジュアルに関するテーマを定義するクラスです。

下記のクラス定義を見ていただければ分かるとおり、非常に多くの引数を取り、アプリ全体の色やフォント、ボタンの形などを定義することができます。

有効に使うことで、アプリ全体のデザインを一括で管理・変更することができます。

ThemeData
ThemeData ThemeData({
  bool? applyElevationOverlayColor,
  NoDefaultCupertinoThemeData? cupertinoOverrideTheme,
  Iterable<ThemeExtension<dynamic>>? extensions,
  InputDecorationTheme? inputDecorationTheme,
  MaterialTapTargetSize? materialTapTargetSize,
  PageTransitionsTheme? pageTransitionsTheme,
  TargetPlatform? platform,
  ScrollbarThemeData? scrollbarTheme,
  InteractiveInkFeatureFactory? splashFactory,
  VisualDensity? visualDensity,
  bool? useMaterial3,
  ColorScheme? colorScheme,
  Color? colorSchemeSeed,
  Brightness? brightness,
  MaterialColor? primarySwatch,
  Color? primaryColor,
  Color? primaryColorLight,
  Color? primaryColorDark,
  Color? focusColor,
  Color? hoverColor,
  Color? shadowColor,
  Color? canvasColor,
  Color? scaffoldBackgroundColor,
  Color? bottomAppBarColor,
  Color? cardColor,
  Color? dividerColor,
  Color? highlightColor,
  Color? splashColor,
  Color? selectedRowColor,
  Color? unselectedWidgetColor,
  Color? disabledColor,
  Color? secondaryHeaderColor,
  Color? backgroundColor,
  Color? dialogBackgroundColor,
  Color? indicatorColor,
  Color? hintColor,
  Color? errorColor,
  Color? toggleableActiveColor,
  String? fontFamily,
  Typography? typography,
  TextTheme? textTheme,
  TextTheme? primaryTextTheme,
  IconThemeData? iconTheme,
  IconThemeData? primaryIconTheme,
  AppBarTheme? appBarTheme,
  MaterialBannerThemeData? bannerTheme,
  BottomAppBarTheme? bottomAppBarTheme,
  BottomNavigationBarThemeData? bottomNavigationBarTheme,
  BottomSheetThemeData? bottomSheetTheme,
  ButtonBarThemeData? buttonBarTheme,
  ButtonThemeData? buttonTheme,
  CardTheme? cardTheme,
  CheckboxThemeData? checkboxTheme,
  ChipThemeData? chipTheme,
  DataTableThemeData? dataTableTheme,
  DialogTheme? dialogTheme,
  DividerThemeData? dividerTheme,
  DrawerThemeData? drawerTheme,
  ElevatedButtonThemeData? elevatedButtonTheme,
  FloatingActionButtonThemeData? floatingActionButtonTheme,
  ListTileThemeData? listTileTheme,
  NavigationBarThemeData? navigationBarTheme,
  NavigationRailThemeData? navigationRailTheme,
  OutlinedButtonThemeData? outlinedButtonTheme,
  PopupMenuThemeData? popupMenuTheme,
  ProgressIndicatorThemeData? progressIndicatorTheme,
  RadioThemeData? radioTheme,
  SliderThemeData? sliderTheme,
  SnackBarThemeData? snackBarTheme,
  SwitchThemeData? switchTheme,
  TabBarTheme? tabBarTheme,
  TextButtonThemeData? textButtonTheme,
  TextSelectionThemeData? textSelectionTheme,
  TimePickerThemeData? timePickerTheme,
  ToggleButtonsThemeData? toggleButtonsTheme,
  TooltipThemeData? tooltipTheme,
  ExpansionTileThemeData? expansionTileTheme,
  bool? useTextSelectionTheme,
  Color? textSelectionColor,
  Color? cursorColor,
  Color? textSelectionHandleColor,
  Color? accentColor,
  Brightness? accentColorBrightness,
  TextTheme? accentTextTheme,
  IconThemeData? accentIconTheme,
  Color? buttonColor,
  bool? fixTextFieldOutlineLabel,
  Brightness? primaryColorBrightness,
  AndroidOverscrollIndicator? androidOverscrollIndicator,
})

Key

1つ1つの Widget を一意に特定する為の識別子です。

アプリは非常に多くの Widget で構成されます。時には同じ Widget が複数並ぶような場合もあります。このような場合、Flutter はどの Widget を操作であったり、更新すべきかであったりを判断する必要があり、その際に用いられるのが Keyクラス です。

nullable な引数なので、必ず渡す必要はありませんが StatelessWidget、StatefulWidget を定義する際は必ず引数として記述します。

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

MyHomePageクラス

前述したStatefulWidgetを継承したクラスです。StatefulWidgetについては前項を参照していただくとして、今回はMyHomepageクラスが返している Widget 群を見ていきましょう。

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

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

Scaffold Widget

Scaffoldwidget はマテリアルデザインのアプリを作る(MaterialAppを使う)場合に、ページの元となる widget です。「白紙のページ」と考えるとイメージがつきやすいかと思います。bodyに渡す Widget がページの中身になります。

その他にもマテリアデザインのページでよく使われれる AppBarFloatingActionButtonDrawerBottomNavigationBar などの Widget を対応するフィールドに渡すことで、それらをページにレイアウトしてくれます。

scaffold
(new) Scaffold Scaffold({
  Key? key,
  PreferredSizeWidget? appBar,
  Widget? body,
  Widget? floatingActionButton,
  FloatingActionButtonLocation? floatingActionButtonLocation,
  FloatingActionButtonAnimator? floatingActionButtonAnimator,
  List<Widget>? persistentFooterButtons,
  AlignmentDirectional persistentFooterAlignment = AlignmentDirectional.centerEnd,
  Widget? drawer,
  void Function(bool)? onDrawerChanged,
  Widget? endDrawer,
  void Function(bool)? onEndDrawerChanged,
  Widget? bottomNavigationBar,
  Widget? bottomSheet,
  Color? backgroundColor,
  bool? resizeToAvoidBottomInset,
  bool primary = true,
  DragStartBehavior drawerDragStartBehavior = DragStartBehavior.start,
  bool extendBody = false,
  bool extendBodyBehindAppBar = false,
  Color? drawerScrimColor,
  double? drawerEdgeDragWidth,
  bool drawerEnableOpenDragGesture = true,
  bool endDrawerEnableOpenDragGesture = true,
  String? restorationId,
})

AppBar Widget

画面上部のアプリバーを表示する Widget です。titleに渡す Widget がアプリバーのタイトルになります。

  appBar: AppBar(
    title: Text(widget.title),
  ),

Center Widget

名前の通り、子 Widget を中央に配置する Widget です。

CenterAppBarの様に UI コンポーネントを描画する Widget ではなく、レイアウトのみを行う Widget です。Flutter にはこのようなレイアウトのみを行う Widget も多数用意されています。

  body: Center(
    child: Widget(),
  ),

Column Widget

縦に子 Widget を配置する Widget です。こちらもCenterと同様に、レイアウトのみを行う Widget です。

Columnは単一の子 Widget ではなく、複数の子 Widget を持つことができます。受け取った複数の子 Widget をカラム(列)に並べます。その他にもその子 Widget をどのように配置するかを指定する様々なプロパティも用意されています。

    Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Widget(),
        Widget(),
      ],
    ),

Text Widget

名前通り、文字列を表示する Widget です。フォントや文字色、文字サイズなどのスタイルを指定するフィールドも用意されています。第一引数に渡された文字列を表示します。

    const Text(
      'You have pushed the button this many times:',
    ),

FloatingActionButton Widget

画面下部の画面の上に浮いたようなボタンを表示する Widget です。

Flutter にはこのFloatingActionButtonという Widget だけでなく、いくつかのボタン Widget が用意されています。これらのボタン Widget は、ユーザーのタップを検知することが出来、タップされた際に実行する処理をonPressedフィールドに渡すことができます。

    floatingActionButton: FloatingActionButton(
    onPressed: _incrementCounter, // タップ時に実行する処理
    tooltip: 'Increment',
    child: Icon(Icons.add),
  ),

  ...

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

IconWidget

Iconwidget はアイコンを表示する為の Widget です。第一引数に渡している Iconsクラスには、マテリアルデザインのアイコンが多数定義されている ので、手軽にアイコンを使うことができます。

このサンプルアプリでは+マークのアイコンを表示しています。

    Icon(
      Icons.add,
      color: Colors.white,
    ),

まとめ

今回は簡単なサンプルアプリの画面にも沢山の Widget が使われていることをお分かり頂けたと思います。

Flutter には非常に多くの Widget が用意されており、全てを覚えておくのはなかなか困難です。ざっくりとどんな Widget が存在するかを把握しておいて、使う際には公式ドキュメントを参照すると良いでしょう。

それでは次は画面遷移について見ていきましょう。