Dartのpartを使ってWidgetを分割する

はじめに

【Flutter】肥大化したWidgetファイルを part でスマートに整理する方法

こちらの記事でpartを使ってWidgetのファイルを分割する方法を紹介していた。自分の個人プロダクトでもよく行なっているので紹介したい。

そもそもpartって?

https://idonuntius.com/blogs/dart_import

詳しくは自分のブログで紹介しているので見ていただけると幸いです。 簡単に言うと1つファイルを分割するために使います。partを使うと、1つのファイルの一部として別のファイルを組み込めます。

自分がやっていること

riverpodを使っている際、loadingやerrorのWidgetは使い回すので共通コンポーネントとして定義していますが、dataの際はWidgetはprivateでそのファイル内で定義しています。

lib/root_screen.dart

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('RootScreen')),
      body: Consumer(
        builder: (context, ref, widget) {
          final state = ref.watch(partArticleFutureProvider);
          return state.when(
            data: (article) => _Successful(article: article),
            loading: () => LoadingContainer(),
            error: (e, _) => ErrorContainer(),
          );
        },
      ),
    );
  }
}

class _Successful extends StatelessWidget {
  const _Successful();

  @override
  Widget build(BuildContext context) {
    return ...;
  }
}

但し行数が多い場合は可読性の観点からpartを使って分割しています。

lib/root_screen.dart

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

part 'root_screen.parts.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('RootScreen')),
      body: Consumer(
        builder: (context, ref, widget) {
          final state = ref.watch(partArticleFutureProvider);
          return state.when(
            data: (article) => _Successful(article: article),
            loading: () => LoadingContainer(),
            error: (e, _) => ErrorContainer(),
          );
        },
      ),
    );
  }
}

lib/root_screen.parts.dart

part of 'root_screen.dart';

class _Successful extends StatelessWidget {
  const _Successful();

  @override
  Widget build(BuildContext context) {
    return ...;
  }
}

メリット

一番のメリットは _Successful クラスをprivateのままに分割できることだと思います。
使い回さないけど、行数が多く可読性の観点から分割したいという際に、publicで定義する必要があり、他のクラスからも参照できる形になってしまいます。 しかしpartを使用することによって、privateのままにしておくことができます。

partを使っても良いのか?

EffectiveDartを見てみると下記と書いてありました。

Many Dart developers avoid using part entirely. They find it easier to reason about their code when each library is a single file. If you do choose to use part to split part of a library out into another file, Dart requires the other file to in turn indicate which library it's a part of. https://dart.dev/effective-dart/usage#do-use-strings-in-part-of-directives

多くのDart開発者は1つのファイルである方がコードを見つけやすいので、partを使うことを避けているとのことです。
なので乱用するのではなく、行数が多くなり可読性の観点でpartを使うのはOKなのかと考えています。
(考えが間違っていましたら教えてください)

最後に

partを使うことによってクラスはprivateのまま分割できるのはかなりのメリットではないかと思いました。
ただ乱用は禁止です。