From 5a755823a5931983335a9065b8e5b28aeb4afcb7 Mon Sep 17 00:00:00 2001 From: mathru Date: Fri, 3 Mar 2023 22:07:52 +0900 Subject: [PATCH] feat: Aggregate all dynamic listings into a UniversalListView. --- packages/masamune/lib/masamune.dart | 4 +- .../lib/universal/universal_column.dart | 233 ++++++++++ .../lib/universal/universal_grid_builder.dart | 420 ------------------ .../lib/universal/universal_grid_view.dart | 410 ----------------- .../lib/universal/universal_list_builder.dart | 300 ------------- .../lib/universal/universal_list_view.dart | 73 ++- .../lib/universal/universal_scaffold.dart | 22 +- .../lib/universal/universal_side_bar.dart | 22 +- packages/masamune/pubspec.lock | 8 +- 9 files changed, 335 insertions(+), 1157 deletions(-) create mode 100644 packages/masamune/lib/universal/universal_column.dart delete mode 100644 packages/masamune/lib/universal/universal_grid_builder.dart delete mode 100644 packages/masamune/lib/universal/universal_grid_view.dart delete mode 100644 packages/masamune/lib/universal/universal_list_builder.dart diff --git a/packages/masamune/lib/masamune.dart b/packages/masamune/lib/masamune.dart index dfddd3627..f01bccc02 100644 --- a/packages/masamune/lib/masamune.dart +++ b/packages/masamune/lib/masamune.dart @@ -61,10 +61,8 @@ part 'universal/universal_scope.dart'; part 'universal/universal_scaffold.dart'; part 'universal/universal_app_bar.dart'; part 'universal/universal_list_view.dart'; -part 'universal/universal_grid_view.dart'; -part 'universal/universal_list_builder.dart'; -part 'universal/universal_grid_builder.dart'; part 'universal/universal_container.dart'; part 'universal/universal_masamune_adapter.dart'; part 'universal/universal_side_bar.dart'; part 'universal/extensions.dart'; +part 'universal/universal_column.dart'; diff --git a/packages/masamune/lib/universal/universal_column.dart b/packages/masamune/lib/universal/universal_column.dart new file mode 100644 index 000000000..a09a2bf52 --- /dev/null +++ b/packages/masamune/lib/universal/universal_column.dart @@ -0,0 +1,233 @@ +part of masamune; + +/// Create responsive columns. +/// +/// It is recommended to place it directly under [UniversalScaffold.body]. +/// Otherwise, [Column] is fine. +/// +/// The maximum width is automatically set according to [UniversalScaffold.breakpoint]. +/// +/// レスポンシブに対応したカラムを作成します。 +/// +/// [UniversalScaffold.body]の直下に置くことをおすすめします。 +/// それ以外は[Column]で問題ございません。 +/// +/// [UniversalScaffold.breakpoint]に応じて最大の横幅が自動で設定されます。 +class UniversalColumn extends StatelessWidget { + /// Create responsive columns. + /// + /// It is recommended to place it directly under [UniversalScaffold.body]. + /// Otherwise, [Column] is fine. + /// + /// The maximum width is automatically set according to [UniversalScaffold.breakpoint]. + /// + /// レスポンシブに対応したカラムを作成します。 + /// + /// [UniversalScaffold.body]の直下に置くことをおすすめします。 + /// それ以外は[Column]で問題ございません。 + /// + /// [UniversalScaffold.breakpoint]に応じて最大の横幅が自動で設定されます。 + const UniversalColumn({ + super.key, + required this.children, + this.padding, + this.margin, + this.color, + this.decoration, + this.foregroundDecoration, + this.width, + this.height, + this.constraints, + this.transform, + this.transformAlignment, + this.clipBehavior = Clip.none, + this.paddingWhenNotFullWidth, + this.breakpoint, + this.alignment = Alignment.center, + this.mainAxisAlignment = MainAxisAlignment.start, + this.crossAxisAlignment = CrossAxisAlignment.center, + this.mainAxisSize = MainAxisSize.max, + this.verticalDirection = VerticalDirection.down, + }); + + /// You can specify the breakpoint at which the UI will change to a mobile-oriented UI. + /// + /// UIがモバイル向けのUIに変化するブレークポイントを指定できます。 + final ResponsiveBreakpoint? breakpoint; + + /// [padding] when the width does not exceed [UniversalScaffold.breakpoint] and the width is fixed.If [Null], [padding] is used. + /// + /// 横幅が[UniversalScaffold.breakpoint]を超えない場合、横幅が固定されているときの[padding]。[Null]の場合は[padding]が利用されます。 + final EdgeInsetsGeometry? paddingWhenNotFullWidth; + + /// This value holds the alignment to be used by the container. + /// + /// この値はコンテナに使用される配置を保持します。 + final AlignmentGeometry alignment; + + /// Defines where to place the widget with respect to its parent. + /// + /// 親要素に対してウィジェットを配置する位置を定義します。 + final EdgeInsetsGeometry? padding; + + /// Sets the background color of the container. + /// + /// If [decoration] is specified, set the background color within [decoration]. + /// + /// コンテナの背景色を設定します。 + /// + /// [decoration]が指定されている場合は、[decoration]内で背景色を設定してください。 + final Color? color; + + /// Sets the container background decoration. + /// + /// コンテナの背景の装飾を設定します。 + final Decoration? decoration; + + /// Sets the decorations to be drawn before the container's [builder]. + /// + /// コンテナの[builder]より前に描画される装飾を設定します。 + final Decoration? foregroundDecoration; + + /// Sets the width of the container. + /// + /// Takes precedence over [breakpoint] and [constraints]. + /// + /// コンテナの横幅を設定します。 + /// + /// [breakpoint]や[constraints]より優先されます。 + final double? width; + + /// Sets the height of the container. + /// + /// Takes precedence over [breakpoint] and [constraints]. + /// + /// コンテナの縦幅を設定します。 + /// + /// [breakpoint]や[constraints]より優先されます。 + final double? height; + + /// Sets size constraints for containers. + /// + /// If [breakpoint] is set, it takes precedence. + /// + /// コンテナのサイズ制約を設定します。 + /// + /// [breakpoint]が設定されている場合はそれが優先されます。 + final BoxConstraints? constraints; + + /// Sets the margin of the container. + /// + /// コンテナのマージンを設定します。 + final EdgeInsetsGeometry? margin; + + /// The transformation matrix to apply before painting the container. + /// + /// コンテナの描画前に適用する変換行列。 + final Matrix4? transform; + + /// The alignment of the origin, relative to the size of the container, if [transform] is specified. + /// + /// When [transform] is null, the value of this property is ignored. + /// + /// [transform]が指定されている場合、コンテナのサイズに対する原点の配置を設定します。 + /// + /// [transform]がnullの場合、このプロパティの値は無視されます。 + /// + /// See also: + /// + /// * [Transform.alignment], which is set by this property. + final AlignmentGeometry? transformAlignment; + + /// The clip behavior when [Container.decoration] is not null. + /// + /// Defaults to [Clip.none]. Must be [Clip.none] if [decoration] is null. + /// + /// If a clip is to be applied, the [Decoration.getClipPath] method + /// for the provided decoration must return a clip path. (This is not + /// supported by all decorations; the default implementation of that + /// method throws an [UnsupportedError].) + /// + /// [Container.decoration]がnullでない場合のクリップの振る舞い。 + /// + /// デフォルトは[Clip.none]です。[decoration]がnullの場合は[Clip.none]である必要があります。 + /// + /// クリップを適用する場合、提供された装飾の[Decoration.getClipPath]メソッドはクリップパスを返す必要があります。 + /// (これはすべての装飾に対応していません。そのメソッドのデフォルト実装は[UnsupportedError]をスローします。) + final Clip clipBehavior; + + /// Widgets to be stored in [Container]. + /// + /// [Container]の中に格納するウィジェット。 + final List children; + + /// Sets the column's main axial alignment. + /// + /// カラムのメイン軸方向の配置を設定します。 + final MainAxisAlignment mainAxisAlignment; + + /// Sets the column's cross-axis alignment. + /// + /// カラムのクロス軸方向の配置を設定します。 + final CrossAxisAlignment crossAxisAlignment; + + /// Sets the column's main axis size. + /// + /// カラムのメイン軸方向のサイズを設定します。 + final MainAxisSize mainAxisSize; + + /// Sets the column's vertical direction. + /// + /// カラムの垂直方向の配置を設定します。 + final VerticalDirection verticalDirection; + + @override + Widget build(BuildContext context) { + final breakpoint = + this.breakpoint ?? ResponsiveScaffold.of(context)?.breakpoint; + + return Align( + alignment: alignment, + child: Container( + constraints: + constraints?.copyWith(maxWidth: breakpoint?.width(context)) ?? + BoxConstraints( + maxWidth: breakpoint?.width(context) ?? double.infinity, + ), + padding: _effectivePadding(context, breakpoint), + margin: margin, + color: color, + decoration: decoration, + foregroundDecoration: foregroundDecoration, + width: width, + height: height, + alignment: alignment, + transform: transform, + transformAlignment: transformAlignment, + clipBehavior: clipBehavior, + child: Column( + verticalDirection: verticalDirection, + mainAxisAlignment: mainAxisAlignment, + crossAxisAlignment: crossAxisAlignment, + mainAxisSize: mainAxisSize, + children: children, + ), + ), + ); + } + + EdgeInsetsGeometry? _effectivePadding( + BuildContext context, + ResponsiveBreakpoint? breakpoint, + ) { + if (breakpoint?.width(context) == double.infinity) { + return padding; + } else { + final universal = + MasamuneAdapterScope.of(context); + return paddingWhenNotFullWidth ?? + universal?.defaultBodyPaddingWhenNotFullWidth ?? + padding; + } + } +} diff --git a/packages/masamune/lib/universal/universal_grid_builder.dart b/packages/masamune/lib/universal/universal_grid_builder.dart deleted file mode 100644 index 9382d685b..000000000 --- a/packages/masamune/lib/universal/universal_grid_builder.dart +++ /dev/null @@ -1,420 +0,0 @@ -part of masamune; - -/// Create a [GridView] to provide a consistent UI across web, desktop, and mobile. -/// -/// The [UniversalAppBar] and [UniversalSliverAppBar] will automatically select and set whether to use the normal list or the Sliver list. -/// -/// Otherwise, it can be used in the same way as a normal [GridView]. -/// -/// You can expand the grid according to the number of horizontal elements with [UniversalGridView.count] and according to the maximum width with [UniversalGridView.extent]. -/// -/// When [onRefresh] is specified, [RefreshIndicator] is automatically set. -/// -/// Setting [showScrollbarWhenDesktopOrWeb] to `true` will show scrollbars on desktop and web. -/// -/// It is responsive and the maximum width is set by [UniversalScaffold.breakpoint]. -/// -/// If [listenWhenListenable] is `true`, [ListenableListener] will be wrapped around each element if [source] inherits [Listenable]. -/// -/// Therefore, each element of [source] is monitored individually, and if any element is updated, only that element is updated in the drawing. -/// -/// Webとデスクトップ、モバイルで一貫したUIを提供するための[GridView]を作成します。 -/// -/// [UniversalAppBar]、[UniversalSliverAppBar]によって通常のリストかSliverのリストかを自動で選択して設定します。 -/// -/// その他は通常の[GridView]と同じように利用可能です。 -/// -/// [UniversalGridView.count]で横の要素数に応じたグリッドを展開することができ、[UniversalGridView.extent]で最大横幅に応じたグリッドを展開することができます。 -/// -/// [onRefresh]を指定すると[RefreshIndicator]を自動で設定します。 -/// -/// [showScrollbarWhenDesktopOrWeb]を`true`にするとデスクトップとWebでスクロールバーを表示します。 -/// -/// レスポンシブ対応しており[UniversalScaffold.breakpoint]によって最大の横幅が設定されます。 -/// -/// [listenWhenListenable]が`true`になっている場合、[source]に[Listenable]を継承している場合[ListenableListener]が各要素にラップされます。 -/// そのため、[source]の各要素をそれぞれ監視し、いずれかの要素が更新された場合その要素のみ描画が更新されます。 -class UniversalGridBuilder extends GridBuilder { - /// Create a [GridView] to provide a consistent UI across web, desktop, and mobile. - /// - /// The [UniversalAppBar] and [UniversalSliverAppBar] will automatically select and set whether to use the normal list or the Sliver list. - /// - /// Otherwise, it can be used in the same way as a normal [GridView]. - /// - /// You can expand the grid according to the number of horizontal elements with [UniversalGridView.count] and according to the maximum width with [UniversalGridView.extent]. - /// - /// When [onRefresh] is specified, [RefreshIndicator] is automatically set. - /// - /// Setting [showScrollbarWhenDesktopOrWeb] to `true` will show scrollbars on desktop and web. - /// - /// It is responsive and the maximum width is set by [UniversalScaffold.breakpoint]. - /// - /// If [listenWhenListenable] is `true`, [ListenableListener] will be wrapped around each element if [source] inherits [Listenable]. - /// - /// Therefore, each element of [source] is monitored individually, and if any element is updated, only that element is updated in the drawing. - /// - /// Webとデスクトップ、モバイルで一貫したUIを提供するための[GridView]を作成します。 - /// - /// [UniversalAppBar]、[UniversalSliverAppBar]によって通常のリストかSliverのリストかを自動で選択して設定します。 - /// - /// その他は通常の[GridView]と同じように利用可能です。 - /// - /// [UniversalGridView.count]で横の要素数に応じたグリッドを展開することができ、[UniversalGridView.extent]で最大横幅に応じたグリッドを展開することができます。 - /// - /// [onRefresh]を指定すると[RefreshIndicator]を自動で設定します。 - /// - /// [showScrollbarWhenDesktopOrWeb]を`true`にするとデスクトップとWebでスクロールバーを表示します。 - /// - /// レスポンシブ対応しており[UniversalScaffold.breakpoint]によって最大の横幅が設定されます。 - /// - /// [listenWhenListenable]が`true`になっている場合、[source]に[Listenable]を継承している場合[ListenableListener]が各要素にラップされます。 - /// そのため、[source]の各要素をそれぞれ監視し、いずれかの要素が更新された場合その要素のみ描画が更新されます。 - const UniversalGridBuilder.count({ - Key? key, - Axis scrollDirection = Axis.vertical, - bool reverse = false, - ScrollController? controller, - bool? primary, - ScrollPhysics? physics, - bool shrinkWrap = false, - EdgeInsetsGeometry? padding, - required List source, - required int crossAxisCount, - required Widget Function(BuildContext context, T item, int index) builder, - bool addAutomaticKeepAlives = true, - bool addRepaintBoundaries = true, - bool addSemanticIndexes = true, - double? cacheExtent, - int? semanticChildCount, - bool listenWhenListenable = true, - DragStartBehavior dragStartBehavior = DragStartBehavior.start, - ScrollViewKeyboardDismissBehavior keyboardDismissBehavior = - ScrollViewKeyboardDismissBehavior.manual, - String? restorationId, - Clip clipBehavior = Clip.hardEdge, - double mainAxisSpacing = 0.0, - double crossAxisSpacing = 0.0, - double childAspectRatio = 1.0, - double? mainAxisExtent, - this.center, - this.anchor = 0.0, - this.scrollBehavior, - this.onRefresh, - this.showScrollbarWhenDesktopOrWeb = true, - this.paddingWhenNotFullWidth, - this.decoration, - }) : super.count( - key: key, - scrollDirection: scrollDirection, - reverse: reverse, - controller: controller, - primary: primary, - physics: physics, - shrinkWrap: shrinkWrap, - padding: padding, - source: source, - crossAxisCount: crossAxisCount, - builder: builder, - addAutomaticKeepAlives: addAutomaticKeepAlives, - addRepaintBoundaries: addRepaintBoundaries, - addSemanticIndexes: addSemanticIndexes, - cacheExtent: cacheExtent, - semanticChildCount: semanticChildCount, - listenWhenListenable: listenWhenListenable, - dragStartBehavior: dragStartBehavior, - keyboardDismissBehavior: keyboardDismissBehavior, - restorationId: restorationId, - clipBehavior: clipBehavior, - mainAxisSpacing: mainAxisSpacing, - crossAxisSpacing: crossAxisSpacing, - childAspectRatio: childAspectRatio, - mainAxisExtent: mainAxisExtent, - ); - - /// Create a [GridView] to provide a consistent UI across web, desktop, and mobile. - /// - /// The [UniversalAppBar] and [UniversalSliverAppBar] will automatically select and set whether to use the normal list or the Sliver list. - /// - /// Otherwise, it can be used in the same way as a normal [GridView]. - /// - /// You can expand the grid according to the number of horizontal elements with [UniversalGridView.count] and according to the maximum width with [UniversalGridView.extent]. - /// - /// When [onRefresh] is specified, [RefreshIndicator] is automatically set. - /// - /// Setting [showScrollbarWhenDesktopOrWeb] to `true` will show scrollbars on desktop and web. - /// - /// It is responsive and the maximum width is set by [UniversalScaffold.breakpoint]. - /// - /// If [listenWhenListenable] is `true`, [ListenableListener] will be wrapped around each element if [source] inherits [Listenable]. - /// - /// Therefore, each element of [source] is monitored individually, and if any element is updated, only that element is updated in the drawing. - /// - /// Webとデスクトップ、モバイルで一貫したUIを提供するための[GridView]を作成します。 - /// - /// [UniversalAppBar]、[UniversalSliverAppBar]によって通常のリストかSliverのリストかを自動で選択して設定します。 - /// - /// その他は通常の[GridView]と同じように利用可能です。 - /// - /// [UniversalGridView.count]で横の要素数に応じたグリッドを展開することができ、[UniversalGridView.extent]で最大横幅に応じたグリッドを展開することができます。 - /// - /// [onRefresh]を指定すると[RefreshIndicator]を自動で設定します。 - /// - /// [showScrollbarWhenDesktopOrWeb]を`true`にするとデスクトップとWebでスクロールバーを表示します。 - /// - /// レスポンシブ対応しており[UniversalScaffold.breakpoint]によって最大の横幅が設定されます。 - /// - /// [listenWhenListenable]が`true`になっている場合、[source]に[Listenable]を継承している場合[ListenableListener]が各要素にラップされます。 - /// そのため、[source]の各要素をそれぞれ監視し、いずれかの要素が更新された場合その要素のみ描画が更新されます。 - const UniversalGridBuilder.extent({ - Key? key, - Axis scrollDirection = Axis.vertical, - bool reverse = false, - ScrollController? controller, - bool? primary, - ScrollPhysics? physics, - bool shrinkWrap = false, - EdgeInsetsGeometry? padding, - required List source, - required double maxCrossAxisExtent, - required Widget Function(BuildContext context, T item, int index) builder, - bool addAutomaticKeepAlives = true, - bool addRepaintBoundaries = true, - bool addSemanticIndexes = true, - double? cacheExtent, - int? semanticChildCount, - bool listenWhenListenable = true, - DragStartBehavior dragStartBehavior = DragStartBehavior.start, - ScrollViewKeyboardDismissBehavior keyboardDismissBehavior = - ScrollViewKeyboardDismissBehavior.manual, - String? restorationId, - Clip clipBehavior = Clip.hardEdge, - double mainAxisSpacing = 0.0, - double crossAxisSpacing = 0.0, - double childAspectRatio = 1.0, - double? mainAxisExtent, - this.center, - this.anchor = 0.0, - this.scrollBehavior, - this.onRefresh, - this.showScrollbarWhenDesktopOrWeb = true, - this.paddingWhenNotFullWidth, - this.decoration, - }) : super.extent( - key: key, - scrollDirection: scrollDirection, - reverse: reverse, - controller: controller, - primary: primary, - physics: physics, - shrinkWrap: shrinkWrap, - padding: padding, - source: source, - maxCrossAxisExtent: maxCrossAxisExtent, - builder: builder, - addAutomaticKeepAlives: addAutomaticKeepAlives, - addRepaintBoundaries: addRepaintBoundaries, - addSemanticIndexes: addSemanticIndexes, - listenWhenListenable: listenWhenListenable, - cacheExtent: cacheExtent, - semanticChildCount: semanticChildCount, - dragStartBehavior: dragStartBehavior, - keyboardDismissBehavior: keyboardDismissBehavior, - restorationId: restorationId, - clipBehavior: clipBehavior, - mainAxisSpacing: mainAxisSpacing, - crossAxisSpacing: crossAxisSpacing, - childAspectRatio: childAspectRatio, - mainAxisExtent: mainAxisExtent, - ); - - /// [padding] when the width does not exceed [UniversalScaffold.breakpoint] and the width is fixed.If [Null], [padding] is used. - /// - /// 横幅が[UniversalScaffold.breakpoint]を超えない場合、横幅が固定されているときの[padding]。[Null]の場合は[padding]が利用されます。 - final EdgeInsetsGeometry? paddingWhenNotFullWidth; - - /// Method called by [RefreshIndicator]. - /// - /// Pull-to-Refresh will execute and display an indicator until [Future] is returned. - /// - /// [RefreshIndicator]で呼ばれるメソッド。 - /// - /// Pull-to-Refreshを行うと実行され、[Future]が返されるまでインジケーターを表示します。 - final Future Function()? onRefresh; - - /// Setting this to `true` will display scrollbars on desktop and web. - /// - /// これを`true`にするとデスクトップとWebでスクロールバーを表示します。 - final bool showScrollbarWhenDesktopOrWeb; - - /// Sets the container background decoration. - /// - /// コンテナの背景の装飾を設定します。 - final Decoration? decoration; - - /// The first child in the [GrowthDirection.forward] growth direction. - /// - /// Children after [center] will be placed in the [AxisDirection] determined - /// by [scrollDirection] and [reverse] relative to the [center]. Children - /// before [center] will be placed in the opposite of the axis direction - /// relative to the [center]. This makes the [center] the inflection point of - /// the growth direction. - /// - /// The [center] must be the key of one of the slivers built by [buildSlivers]. - /// - /// Of the built-in subclasses of [ScrollView], only [CustomScrollView] - /// supports [center]; for that class, the given key must be the key of one of - /// the slivers in the [CustomScrollView.slivers] list. - /// - /// See also: - /// - /// * [anchor], which controls where the [center] as aligned in the viewport. - final Key? center; - - /// {@template flutter.widgets.scroll_view.anchor} - /// The relative position of the zero scroll offset. - /// - /// For example, if [anchor] is 0.5 and the [AxisDirection] determined by - /// [scrollDirection] and [reverse] is [AxisDirection.down] or - /// [AxisDirection.up], then the zero scroll offset is vertically centered - /// within the viewport. If the [anchor] is 1.0, and the axis direction is - /// [AxisDirection.right], then the zero scroll offset is on the left edge of - /// the viewport. - /// {@endtemplate} - final double anchor; - - /// {@template flutter.widgets.shadow.scrollBehavior} - /// [ScrollBehavior]s also provide [ScrollPhysics]. If an explicit - /// [ScrollPhysics] is provided in [physics], it will take precedence, - /// followed by [scrollBehavior], and then the inherited ancestor - /// [ScrollBehavior]. - /// {@endtemplate} - final ScrollBehavior? scrollBehavior; - - @override - Widget build(BuildContext context) { - return _buildRefreshIndicator( - context, - _buildScrollbar( - context, - _buildDecoratedBox( - context, - CustomScrollView( - key: key, - scrollDirection: scrollDirection, - reverse: reverse, - controller: this.controller, - primary: primary, - physics: physics, - scrollBehavior: scrollBehavior, - shrinkWrap: shrinkWrap, - center: center, - anchor: anchor, - cacheExtent: cacheExtent, - semanticChildCount: semanticChildCount, - dragStartBehavior: dragStartBehavior, - keyboardDismissBehavior: keyboardDismissBehavior, - restorationId: restorationId, - clipBehavior: clipBehavior, - slivers: [ - _padding( - context, - SliverGrid( - gridDelegate: crossAxisCount == 0 - ? SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: maxCrossAxisExtent, - mainAxisSpacing: mainAxisSpacing, - crossAxisSpacing: crossAxisSpacing, - childAspectRatio: childAspectRatio, - mainAxisExtent: mainAxisExtent, - ) - : SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: crossAxisCount, - mainAxisSpacing: mainAxisSpacing, - crossAxisSpacing: crossAxisSpacing, - childAspectRatio: childAspectRatio, - mainAxisExtent: mainAxisExtent, - ), - delegate: SliverChildBuilderDelegate( - _builder, - childCount: source.length, - ), - ), - ) - ], - ), - ), - ), - ); - } - - Widget _buildRefreshIndicator(BuildContext context, Widget child) { - if (onRefresh != null) { - return RefreshIndicator( - onRefresh: onRefresh!, - child: child, - ); - } else { - return child; - } - } - - Widget _buildScrollbar(BuildContext context, Widget child) { - if (showScrollbarWhenDesktopOrWeb && - (UniversalPlatform.isDesktop || UniversalPlatform.isWeb)) { - return Scrollbar( - interactive: true, - trackVisibility: true, - thumbVisibility: true, - child: child, - ); - } else { - return child; - } - } - - Widget _buildDecoratedBox(BuildContext context, Widget child) { - if (decoration != null) { - return DecoratedBox( - decoration: decoration!, - child: child, - ); - } else { - return child; - } - } - - Widget _padding(BuildContext context, Widget sliver) { - final width = MediaQuery.of(context).size.width; - final breakpoint = ResponsiveScaffold.of(context)?.breakpoint; - final maxWidth = (breakpoint?.width(context) ?? width).limitHigh(width); - final responsivePadding = (width - maxWidth) / 2.0; - final resolvedPadding = - _effectivePadding(context, breakpoint)?.resolve(TextDirection.ltr); - final generatedPadding = EdgeInsets.fromLTRB( - (resolvedPadding?.left ?? 0.0) + responsivePadding, - resolvedPadding?.top ?? 0.0, - (resolvedPadding?.right ?? 0.0) + responsivePadding, - resolvedPadding?.bottom ?? 0.0, - ); - - return SliverPadding( - padding: generatedPadding, - sliver: sliver, - ); - } - - EdgeInsetsGeometry? _effectivePadding( - BuildContext context, - ResponsiveBreakpoint? breakpoint, - ) { - if (breakpoint?.width(context) == double.infinity) { - return padding; - } else { - final universal = - MasamuneAdapterScope.of(context); - return paddingWhenNotFullWidth ?? - universal?.defaultBodyPaddingWhenNotFullWidth ?? - padding; - } - } -} diff --git a/packages/masamune/lib/universal/universal_grid_view.dart b/packages/masamune/lib/universal/universal_grid_view.dart deleted file mode 100644 index 66f687be0..000000000 --- a/packages/masamune/lib/universal/universal_grid_view.dart +++ /dev/null @@ -1,410 +0,0 @@ -part of masamune; - -/// Create a [GridView] to provide a consistent UI across web, desktop, and mobile. -/// -/// The [UniversalAppBar] and [UniversalSliverAppBar] will automatically select and set whether to use the normal list or the Sliver list. -/// -/// Otherwise, it can be used in the same way as a normal [GridView]. -/// -/// You can expand the grid according to the number of horizontal elements with [UniversalGridView.count] and according to the maximum width with [UniversalGridView.extent]. -/// -/// When [onRefresh] is specified, [RefreshIndicator] is automatically set. -/// -/// Setting [showScrollbarWhenDesktopOrWeb] to `true` will show scrollbars on desktop and web. -/// -/// It is responsive and the maximum width is set by [UniversalScaffold.breakpoint]. -/// -/// Webとデスクトップ、モバイルで一貫したUIを提供するための[GridView]を作成します。 -/// -/// [UniversalAppBar]、[UniversalSliverAppBar]によって通常のリストかSliverのリストかを自動で選択して設定します。 -/// -/// その他は通常の[GridView]と同じように利用可能です。 -/// -/// [UniversalGridView.count]で横の要素数に応じたグリッドを展開することができ、[UniversalGridView.extent]で最大横幅に応じたグリッドを展開することができます。 -/// -/// [onRefresh]を指定すると[RefreshIndicator]を自動で設定します。 -/// -/// [showScrollbarWhenDesktopOrWeb]を`true`にするとデスクトップとWebでスクロールバーを表示します。 -/// -/// レスポンシブ対応しており[UniversalScaffold.breakpoint]によって最大の横幅が設定されます。 -class UniversalGridView extends StatelessWidget { - /// Create a [GridView] to provide a consistent UI across web, desktop, and mobile. - /// - /// The [UniversalAppBar] and [UniversalSliverAppBar] will automatically select and set whether to use the normal list or the Sliver list. - /// - /// Otherwise, it can be used in the same way as a normal [GridView]. - /// - /// You can expand the grid according to the number of horizontal elements with [UniversalGridView.count] and according to the maximum width with [UniversalGridView.extent]. - /// - /// When [onRefresh] is specified, [RefreshIndicator] is automatically set. - /// - /// Setting [showScrollbarWhenDesktopOrWeb] to `true` will show scrollbars on desktop and web. - /// - /// It is responsive and the maximum width is set by [UniversalScaffold.breakpoint]. - /// - /// Webとデスクトップ、モバイルで一貫したUIを提供するための[GridView]を作成します。 - /// - /// [UniversalAppBar]、[UniversalSliverAppBar]によって通常のリストかSliverのリストかを自動で選択して設定します。 - /// - /// その他は通常の[GridView]と同じように利用可能です。 - /// - /// [UniversalGridView.count]で横の要素数に応じたグリッドを展開することができ、[UniversalGridView.extent]で最大横幅に応じたグリッドを展開することができます。 - /// - /// [onRefresh]を指定すると[RefreshIndicator]を自動で設定します。 - /// - /// [showScrollbarWhenDesktopOrWeb]を`true`にするとデスクトップとWebでスクロールバーを表示します。 - /// - /// レスポンシブ対応しており[UniversalScaffold.breakpoint]によって最大の横幅が設定されます。 - const UniversalGridView.count({ - super.key, - this.scrollDirection = Axis.vertical, - this.reverse = false, - this.controller, - bool? primary, - ScrollPhysics? physics, - this.scrollBehavior, - this.shrinkWrap = false, - this.center, - this.anchor = 0.0, - this.cacheExtent, - this.semanticChildCount, - this.dragStartBehavior = DragStartBehavior.start, - this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, - this.restorationId, - this.clipBehavior = Clip.hardEdge, - required this.crossAxisCount, - required this.children, - this.mainAxisSpacing = 0.0, - this.crossAxisSpacing = 0.0, - this.childAspectRatio = 1.0, - this.mainAxisExtent, - this.onRefresh, - this.showScrollbarWhenDesktopOrWeb = true, - this.padding, - this.paddingWhenNotFullWidth, - this.decoration, - }) : assert( - !(controller != null && primary == true), - 'Primary ScrollViews obtain their ScrollController via inheritance from a PrimaryScrollController widget. ' - 'You cannot both set primary to true and pass an explicit controller.', - ), - assert(!shrinkWrap || center == null), - assert(anchor >= 0.0 && anchor <= 1.0), - assert(semanticChildCount == null || semanticChildCount >= 0), - primary = primary ?? - controller == null && identical(scrollDirection, Axis.vertical), - physics = physics ?? - (primary == true || - (primary == null && - controller == null && - identical(scrollDirection, Axis.vertical)) - ? const AlwaysScrollableScrollPhysics() - : null), - maxCrossAxisExtent = 0.0; - - /// Create a [GridView] to provide a consistent UI across web, desktop, and mobile. - /// - /// The [UniversalAppBar] and [UniversalSliverAppBar] will automatically select and set whether to use the normal list or the Sliver list. - /// - /// Otherwise, it can be used in the same way as a normal [GridView]. - /// - /// You can expand the grid according to the number of horizontal elements with [UniversalGridView.count] and according to the maximum width with [UniversalGridView.extent]. - /// - /// When [onRefresh] is specified, [RefreshIndicator] is automatically set. - /// - /// Setting [showScrollbarWhenDesktopOrWeb] to `true` will show scrollbars on desktop and web. - /// - /// It is responsive and the maximum width is set by [UniversalScaffold.breakpoint]. - /// - /// Webとデスクトップ、モバイルで一貫したUIを提供するための[GridView]を作成します。 - /// - /// [UniversalAppBar]、[UniversalSliverAppBar]によって通常のリストかSliverのリストかを自動で選択して設定します。 - /// - /// その他は通常の[GridView]と同じように利用可能です。 - /// - /// [UniversalGridView.count]で横の要素数に応じたグリッドを展開することができ、[UniversalGridView.extent]で最大横幅に応じたグリッドを展開することができます。 - /// - /// [onRefresh]を指定すると[RefreshIndicator]を自動で設定します。 - /// - /// [showScrollbarWhenDesktopOrWeb]を`true`にするとデスクトップとWebでスクロールバーを表示します。 - /// - /// レスポンシブ対応しており[UniversalScaffold.breakpoint]によって最大の横幅が設定されます。 - const UniversalGridView.extent({ - super.key, - this.scrollDirection = Axis.vertical, - this.reverse = false, - this.controller, - bool? primary, - ScrollPhysics? physics, - this.scrollBehavior, - this.shrinkWrap = false, - this.center, - this.anchor = 0.0, - this.cacheExtent, - this.semanticChildCount, - this.dragStartBehavior = DragStartBehavior.start, - this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, - this.restorationId, - this.clipBehavior = Clip.hardEdge, - required this.maxCrossAxisExtent, - required this.children, - this.mainAxisSpacing = 0.0, - this.crossAxisSpacing = 0.0, - this.childAspectRatio = 1.0, - this.mainAxisExtent, - this.padding, - this.paddingWhenNotFullWidth, - this.onRefresh, - this.showScrollbarWhenDesktopOrWeb = true, - this.decoration, - }) : assert( - !(controller != null && primary == true), - 'Primary ScrollViews obtain their ScrollController via inheritance from a PrimaryScrollController widget. ' - 'You cannot both set primary to true and pass an explicit controller.', - ), - assert(!shrinkWrap || center == null), - assert(anchor >= 0.0 && anchor <= 1.0), - assert(semanticChildCount == null || semanticChildCount >= 0), - primary = primary ?? - controller == null && identical(scrollDirection, Axis.vertical), - physics = physics ?? - (primary == true || - (primary == null && - controller == null && - identical(scrollDirection, Axis.vertical)) - ? const AlwaysScrollableScrollPhysics() - : null), - crossAxisCount = 0; - - /// A list of child elements for display in [ListView]. - /// - /// [ListView]で表示するための子要素のリスト。 - final List children; - - /// Method called by [RefreshIndicator]. - /// - /// Pull-to-Refresh will execute and display an indicator until [Future] is returned. - /// - /// [RefreshIndicator]で呼ばれるメソッド。 - /// - /// Pull-to-Refreshを行うと実行され、[Future]が返されるまでインジケーターを表示します。 - final Future Function()? onRefresh; - - /// Setting this to `true` will display scrollbars on desktop and web. - /// - /// これを`true`にするとデスクトップとWebでスクロールバーを表示します。 - final bool showScrollbarWhenDesktopOrWeb; - - /// Sets the container background decoration. - /// - /// コンテナの背景の装飾を設定します。 - final Decoration? decoration; - - /// Maximum width of the grid's horizontal elements. - /// - /// グリッドの横方向要素の最大横幅。 - final double maxCrossAxisExtent; - - /// Space width between vertical elements of the grid. - /// - /// グリッドの縦方向の要素間のスペース幅。 - final double mainAxisSpacing; - - /// Space width between elements in the horizontal direction of the grid. - /// - /// グリッドの横方向の要素間のスペース幅。 - final double crossAxisSpacing; - - /// Aspect ratio of the child element. - /// - /// 子要素のアスペクト比。 - final double childAspectRatio; - - /// Specifies the width of the main element. - /// - /// メイン要素の幅を指定します。 - final double? mainAxisExtent; - - /// Number of horizontal elements in the grid. - /// - /// グリッドの横方向の要素数。 - final int crossAxisCount; - - /// [padding] when the width does not exceed [UniversalScaffold.breakpoint] and the width is fixed.If [Null], [padding] is used. - /// - /// 横幅が[UniversalScaffold.breakpoint]を超えない場合、横幅が固定されているときの[padding]。[Null]の場合は[padding]が利用されます。 - final EdgeInsetsGeometry? paddingWhenNotFullWidth; - - /// {@macro flutter.widgets.scroll_view.padding} - final EdgeInsetsGeometry? padding; - - /// {@macro flutter.widgets.scroll_view.scrollDirection} - final Axis scrollDirection; - - /// {@macro flutter.widgets.scroll_view.reverse} - final bool reverse; - - /// {@macro flutter.widgets.scroll_view.controller} - final ScrollController? controller; - - /// {@macro flutter.widgets.scroll_view.primary} - final bool primary; - - /// {@macro flutter.widgets.scroll_view.physics} - final ScrollPhysics? physics; - - /// {@macro flutter.widgets.shadow.scrollBehavior} - final ScrollBehavior? scrollBehavior; - - /// {@macro flutter.widgets.scroll_view.shrinkWrap} - final bool shrinkWrap; - - /// {@macro flutter.widgets.scroll_view.center} - final Key? center; - - /// {@macro flutter.widgets.scroll_view.anchor} - final double anchor; - - /// {@macro flutter.rendering.RenderViewportBase.cacheExtent} - final double? cacheExtent; - - /// {@macro flutter.widgets.scroll_view.semanticChildCount} - final int? semanticChildCount; - - /// {@macro flutter.widgets.scrollable.dragStartBehavior} - final DragStartBehavior dragStartBehavior; - - /// {@macro flutter.widgets.scroll_view.keyboardDismissBehavior} - final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior; - - /// {@macro flutter.widgets.scrollable.restorationId} - final String? restorationId; - - /// {@macro flutter.material.Material.clipBehavior} - final Clip clipBehavior; - - @override - Widget build(BuildContext context) { - return _buildRefreshIndicator( - context, - _buildScrollbar( - context, - _buildDecoratedBox( - context, - CustomScrollView( - key: key, - scrollDirection: scrollDirection, - reverse: reverse, - controller: controller, - primary: primary, - physics: physics, - scrollBehavior: scrollBehavior, - shrinkWrap: shrinkWrap, - center: center, - anchor: anchor, - cacheExtent: cacheExtent, - semanticChildCount: semanticChildCount, - dragStartBehavior: dragStartBehavior, - keyboardDismissBehavior: keyboardDismissBehavior, - restorationId: restorationId, - clipBehavior: clipBehavior, - slivers: [ - _padding( - context, - SliverGrid( - gridDelegate: crossAxisCount == 0 - ? SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: maxCrossAxisExtent, - mainAxisSpacing: mainAxisSpacing, - crossAxisSpacing: crossAxisSpacing, - childAspectRatio: childAspectRatio, - mainAxisExtent: mainAxisExtent, - ) - : SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: crossAxisCount, - mainAxisSpacing: mainAxisSpacing, - crossAxisSpacing: crossAxisSpacing, - childAspectRatio: childAspectRatio, - mainAxisExtent: mainAxisExtent, - ), - delegate: SliverChildListDelegate(children), - ), - ) - ], - ), - ), - ), - ); - } - - Widget _buildRefreshIndicator(BuildContext context, Widget child) { - if (onRefresh != null) { - return RefreshIndicator( - onRefresh: onRefresh!, - child: child, - ); - } else { - return child; - } - } - - Widget _buildScrollbar(BuildContext context, Widget child) { - if (showScrollbarWhenDesktopOrWeb && - (UniversalPlatform.isDesktop || UniversalPlatform.isWeb)) { - return Scrollbar( - interactive: true, - trackVisibility: true, - thumbVisibility: true, - child: child, - ); - } else { - return child; - } - } - - Widget _buildDecoratedBox(BuildContext context, Widget child) { - if (decoration != null) { - return DecoratedBox( - decoration: decoration!, - child: child, - ); - } else { - return child; - } - } - - Widget _padding(BuildContext context, Widget sliver) { - final width = MediaQuery.of(context).size.width; - final breakpoint = ResponsiveScaffold.of(context)?.breakpoint; - final maxWidth = (breakpoint?.width(context) ?? width).limitHigh(width); - final responsivePadding = (width - maxWidth) / 2.0; - final resolvedPadding = - _effectivePadding(context, breakpoint)?.resolve(TextDirection.ltr); - final generatedPadding = EdgeInsets.fromLTRB( - (resolvedPadding?.left ?? 0.0) + responsivePadding, - resolvedPadding?.top ?? 0.0, - (resolvedPadding?.right ?? 0.0) + responsivePadding, - resolvedPadding?.bottom ?? 0.0, - ); - - return SliverPadding( - padding: generatedPadding, - sliver: sliver, - ); - } - - EdgeInsetsGeometry? _effectivePadding( - BuildContext context, - ResponsiveBreakpoint? breakpoint, - ) { - if (breakpoint?.width(context) == double.infinity) { - return padding; - } else { - final universal = - MasamuneAdapterScope.of(context); - return paddingWhenNotFullWidth ?? - universal?.defaultBodyPaddingWhenNotFullWidth ?? - padding; - } - } -} diff --git a/packages/masamune/lib/universal/universal_list_builder.dart b/packages/masamune/lib/universal/universal_list_builder.dart deleted file mode 100644 index 9a452b622..000000000 --- a/packages/masamune/lib/universal/universal_list_builder.dart +++ /dev/null @@ -1,300 +0,0 @@ -part of masamune; - -/// Create [ListView] to provide a consistent UI across web, desktop, and mobile. -/// -/// The [UniversalAppBar] and [UniversalSliverAppBar] will automatically select and set whether to use the normal list or the Sliver list. -/// -/// Otherwise, it can be used in the same way as a normal [ListView]. -/// -/// When [onRefresh] is specified, [RefreshIndicator] is automatically set. -/// -/// Setting [showScrollbarWhenDesktopOrWeb] to `true` will show scrollbars on desktop and web. -/// -/// It is responsive and the maximum width is set by [UniversalScaffold.breakpoint]. -/// -/// If [source] is given, each of its elements is displayed side by side in [builder]. -/// -/// If [top] is set, the [top] elements are lined up before the [source] elements are lined up. -/// -/// If [bottom] is set, the elements of [bottom] are lined up after the elements of [source] are lined up. -/// -/// When [insert] is set, [insert] is inserted at the position of [insertPosition] in [source]. -/// -/// If [listenWhenListenable] is `true`, [ListenableListener] will be wrapped around each element if [source] inherits [Listenable]. -/// Therefore, each element of [source] is monitored individually, and if any element is updated, only that element is updated in the drawing. -/// -/// Webとデスクトップ、モバイルで一貫したUIを提供するための[ListView]を作成します。 -/// -/// [UniversalAppBar]、[UniversalSliverAppBar]によって通常のリストかSliverのリストかを自動で選択して設定します。 -/// -/// その他は通常の[ListView]と同じように利用可能です。 -/// -/// [onRefresh]を指定すると[RefreshIndicator]を自動で設定します。 -/// -/// [showScrollbarWhenDesktopOrWeb]を`true`にするとデスクトップとWebでスクロールバーを表示します。 -/// -/// レスポンシブ対応しており[UniversalScaffold.breakpoint]によって最大の横幅が設定されます。 -/// -/// [source]を与えるとその各要素を[builder]で並べて表示します。 -/// -/// [top]を設定すると[source]の要素を並べる前に[top]の要素を並べます。 -/// -/// [bottom]を設定すると[source]の要素を並べた後に[bottom]の要素を並べます。 -/// -/// [insert]を設定すると[source]の[insertPosition]の位置に[insert]が挿入されます。 -/// -/// [listenWhenListenable]が`true`になっている場合、[source]に[Listenable]を継承している場合[ListenableListener]が各要素にラップされます。 -/// そのため、[source]の各要素をそれぞれ監視し、いずれかの要素が更新された場合その要素のみ描画が更新されます。 -class UniversalListBuilder extends ListBuilder { - /// Create [ListView] to provide a consistent UI across web, desktop, and mobile. - /// - /// The [UniversalAppBar] and [UniversalSliverAppBar] will automatically select and set whether to use the normal list or the Sliver list. - /// - /// Otherwise, it can be used in the same way as a normal [ListView]. - /// - /// When [onRefresh] is specified, [RefreshIndicator] is automatically set. - /// - /// Setting [showScrollbarWhenDesktopOrWeb] to `true` will show scrollbars on desktop and web. - /// - /// It is responsive and the maximum width is set by [UniversalScaffold.breakpoint]. - /// - /// If [source] is given, each of its elements is displayed side by side in [builder]. - /// - /// If [top] is set, the [top] elements are lined up before the [source] elements are lined up. - /// - /// If [bottom] is set, the elements of [bottom] are lined up after the elements of [source] are lined up. - /// - /// When [insert] is set, [insert] is inserted at the position of [insertPosition] in [source]. - /// - /// If [listenWhenListenable] is `true`, [ListenableListener] will be wrapped around each element if [source] inherits [Listenable]. - /// Therefore, each element of [source] is monitored individually, and if any element is updated, only that element is updated in the drawing. - /// - /// Webとデスクトップ、モバイルで一貫したUIを提供するための[ListView]を作成します。 - /// - /// [UniversalAppBar]、[UniversalSliverAppBar]によって通常のリストかSliverのリストかを自動で選択して設定します。 - /// - /// その他は通常の[ListView]と同じように利用可能です。 - /// - /// [onRefresh]を指定すると[RefreshIndicator]を自動で設定します。 - /// - /// [showScrollbarWhenDesktopOrWeb]を`true`にするとデスクトップとWebでスクロールバーを表示します。 - /// - /// レスポンシブ対応しており[UniversalScaffold.breakpoint]によって最大の横幅が設定されます。 - /// - /// [source]を与えるとその各要素を[builder]で並べて表示します。 - /// - /// [top]を設定すると[source]の要素を並べる前に[top]の要素を並べます。 - /// - /// [bottom]を設定すると[source]の要素を並べた後に[bottom]の要素を並べます。 - /// - /// [insert]を設定すると[source]の[insertPosition]の位置に[insert]が挿入されます。 - /// - /// [listenWhenListenable]が`true`になっている場合、[source]に[Listenable]を継承している場合[ListenableListener]が各要素にラップされます。 - /// そのため、[source]の各要素をそれぞれ監視し、いずれかの要素が更新された場合その要素のみ描画が更新されます。 - const UniversalListBuilder({ - super.key, - required super.source, - required super.builder, - super.top, - super.insert, - super.insertPosition = 0, - super.bottom, - super.scrollDirection = Axis.vertical, - super.reverse = false, - super.controller, - super.listenWhenListenable = true, - super.primary, - super.physics, - this.scrollBehavior, - super.shrinkWrap = false, - this.center, - this.anchor = 0.0, - super.cacheExtent, - super.semanticChildCount, - super.dragStartBehavior = DragStartBehavior.start, - super.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, - super.restorationId, - super.clipBehavior = Clip.hardEdge, - super.padding, - this.paddingWhenNotFullWidth, - this.onRefresh, - this.showScrollbarWhenDesktopOrWeb = true, - this.decoration, - }); - - /// [padding] when the width does not exceed [UniversalScaffold.breakpoint] and the width is fixed.If [Null], [padding] is used. - /// - /// 横幅が[UniversalScaffold.breakpoint]を超えない場合、横幅が固定されているときの[padding]。[Null]の場合は[padding]が利用されます。 - final EdgeInsetsGeometry? paddingWhenNotFullWidth; - - /// Method called by [RefreshIndicator]. - /// - /// Pull-to-Refresh will execute and display an indicator until [Future] is returned. - /// - /// [RefreshIndicator]で呼ばれるメソッド。 - /// - /// Pull-to-Refreshを行うと実行され、[Future]が返されるまでインジケーターを表示します。 - final Future Function()? onRefresh; - - /// Setting this to `true` will display scrollbars on desktop and web. - /// - /// これを`true`にするとデスクトップとWebでスクロールバーを表示します。 - final bool showScrollbarWhenDesktopOrWeb; - - /// Sets the container background decoration. - /// - /// コンテナの背景の装飾を設定します。 - final Decoration? decoration; - - /// The first child in the [GrowthDirection.forward] growth direction. - /// - /// Children after [center] will be placed in the [AxisDirection] determined - /// by [scrollDirection] and [reverse] relative to the [center]. Children - /// before [center] will be placed in the opposite of the axis direction - /// relative to the [center]. This makes the [center] the inflection point of - /// the growth direction. - /// - /// The [center] must be the key of one of the slivers built by [buildSlivers]. - /// - /// Of the built-in subclasses of [ScrollView], only [CustomScrollView] - /// supports [center]; for that class, the given key must be the key of one of - /// the slivers in the [CustomScrollView.slivers] list. - /// - /// See also: - /// - /// * [anchor], which controls where the [center] as aligned in the viewport. - final Key? center; - - /// {@template flutter.widgets.scroll_view.anchor} - /// The relative position of the zero scroll offset. - /// - /// For example, if [anchor] is 0.5 and the [AxisDirection] determined by - /// [scrollDirection] and [reverse] is [AxisDirection.down] or - /// [AxisDirection.up], then the zero scroll offset is vertically centered - /// within the viewport. If the [anchor] is 1.0, and the axis direction is - /// [AxisDirection.right], then the zero scroll offset is on the left edge of - /// the viewport. - /// {@endtemplate} - final double anchor; - - /// {@template flutter.widgets.shadow.scrollBehavior} - /// [ScrollBehavior]s also provide [ScrollPhysics]. If an explicit - /// [ScrollPhysics] is provided in [physics], it will take precedence, - /// followed by [scrollBehavior], and then the inherited ancestor - /// [ScrollBehavior]. - /// {@endtemplate} - final ScrollBehavior? scrollBehavior; - - @override - Widget build(BuildContext context) { - return _buildRefreshIndicator( - context, - _buildScrollbar( - context, - _buildDecoratedBox( - context, - CustomScrollView( - key: key, - scrollDirection: scrollDirection, - reverse: reverse, - controller: this.controller, - primary: primary, - physics: physics, - scrollBehavior: scrollBehavior, - shrinkWrap: shrinkWrap, - center: center, - anchor: anchor, - cacheExtent: cacheExtent, - semanticChildCount: semanticChildCount, - dragStartBehavior: dragStartBehavior, - keyboardDismissBehavior: keyboardDismissBehavior, - restorationId: restorationId, - clipBehavior: clipBehavior, - slivers: [ - _padding( - context, - SliverList( - delegate: SliverChildBuilderDelegate( - _builder, - childCount: _length, - ), - ), - ) - ], - ), - ), - ), - ); - } - - Widget _buildRefreshIndicator(BuildContext context, Widget child) { - if (onRefresh != null) { - return RefreshIndicator( - onRefresh: onRefresh!, - child: child, - ); - } else { - return child; - } - } - - Widget _buildScrollbar(BuildContext context, Widget child) { - if (showScrollbarWhenDesktopOrWeb && - (UniversalPlatform.isDesktop || UniversalPlatform.isWeb)) { - return Scrollbar( - interactive: true, - trackVisibility: true, - thumbVisibility: true, - child: child, - ); - } else { - return child; - } - } - - Widget _buildDecoratedBox(BuildContext context, Widget child) { - if (decoration != null) { - return DecoratedBox( - decoration: decoration!, - child: child, - ); - } else { - return child; - } - } - - Widget _padding(BuildContext context, Widget sliver) { - final width = MediaQuery.of(context).size.width; - final breakpoint = ResponsiveScaffold.of(context)?.breakpoint; - final maxWidth = (breakpoint?.width(context) ?? width).limitHigh(width); - final responsivePadding = (width - maxWidth) / 2.0; - final resolvedPadding = - _effectivePadding(context, breakpoint)?.resolve(TextDirection.ltr); - final generatedPadding = EdgeInsets.fromLTRB( - (resolvedPadding?.left ?? 0.0) + responsivePadding, - resolvedPadding?.top ?? 0.0, - (resolvedPadding?.right ?? 0.0) + responsivePadding, - resolvedPadding?.bottom ?? 0.0, - ); - - return SliverPadding( - padding: generatedPadding, - sliver: sliver, - ); - } - - EdgeInsetsGeometry? _effectivePadding( - BuildContext context, - ResponsiveBreakpoint? breakpoint, - ) { - if (breakpoint?.width(context) == double.infinity) { - return padding; - } else { - final universal = - MasamuneAdapterScope.of(context); - return paddingWhenNotFullWidth ?? - universal?.defaultBodyPaddingWhenNotFullWidth ?? - padding; - } - } -} diff --git a/packages/masamune/lib/universal/universal_list_view.dart b/packages/masamune/lib/universal/universal_list_view.dart index 1f56b695a..6ff8e1cbd 100644 --- a/packages/masamune/lib/universal/universal_list_view.dart +++ b/packages/masamune/lib/universal/universal_list_view.dart @@ -69,6 +69,8 @@ class UniversalListView extends StatelessWidget { this.restorationId, this.clipBehavior = Clip.hardEdge, this.paddingWhenNotFullWidth, + this.crossAxisAlignment = CrossAxisAlignment.start, + this.rowSegments = 12, this.padding, }) : assert( !(controller != null && primary == true), @@ -165,8 +167,20 @@ class UniversalListView extends StatelessWidget { /// {@macro flutter.material.Material.clipBehavior} final Clip clipBehavior; + /// You can change the placement of [ResponsiveCol]. + /// + /// [ResponsiveCol]の配置を変更することができます。 + final CrossAxisAlignment crossAxisAlignment; + + /// The number of segments in the horizontal direction. + /// + /// 横方向のセグメントの数です。 + final int rowSegments; + @override Widget build(BuildContext context) { + final rows = _createRows(context, children); + return _buildRefreshIndicator( context, _buildScrollbar( @@ -193,8 +207,13 @@ class UniversalListView extends StatelessWidget { slivers: [ _padding( context, - SliverList(delegate: SliverChildListDelegate(children)), - ) + SliverList( + delegate: SliverChildBuilderDelegate( + (context, i) => rows[i], + childCount: rows.length, + ), + ), + ), ], ), ), @@ -202,6 +221,56 @@ class UniversalListView extends StatelessWidget { ); } + List _createRows(BuildContext context, List children) { + int accumulatedWidth = 0; + var cols = []; + final rows = []; + + for (final col in children) { + if (col is ResponsiveCol) { + final colWidth = col.currentConfig(context) ?? 12; + if (accumulatedWidth + colWidth > rowSegments) { + if (accumulatedWidth < rowSegments) { + cols.add(Spacer( + flex: rowSegments - accumulatedWidth, + )); + } + rows.add(Row( + crossAxisAlignment: crossAxisAlignment, + children: cols, + )); + cols = []; + accumulatedWidth = 0; + } + cols.add(col); + accumulatedWidth += colWidth; + } else { + if (cols.isNotEmpty) { + rows.add(Row( + crossAxisAlignment: crossAxisAlignment, + children: cols, + )); + cols = []; + accumulatedWidth = 0; + } + rows.add(col); + } + } + + if (accumulatedWidth >= 0) { + if (accumulatedWidth < rowSegments) { + cols.add(Spacer( + flex: rowSegments - accumulatedWidth, + )); + } + rows.add(Row( + crossAxisAlignment: crossAxisAlignment, + children: cols, + )); + } + return rows; + } + Widget _buildRefreshIndicator(BuildContext context, Widget child) { if (onRefresh != null) { return RefreshIndicator( diff --git a/packages/masamune/lib/universal/universal_scaffold.dart b/packages/masamune/lib/universal/universal_scaffold.dart index dd2dcd92c..64b848e75 100644 --- a/packages/masamune/lib/universal/universal_scaffold.dart +++ b/packages/masamune/lib/universal/universal_scaffold.dart @@ -146,7 +146,6 @@ class UniversalScaffold extends StatefulWidget { this.breakpoint, this.persistentFooterAlignment = AlignmentDirectional.centerEnd, this.sideBar, - this.sideBarWidth = kSideBarWidth, }); /// Setting this to `true` will prevent [body] from being displayed during the transition animation, resulting in a smooth animation. @@ -158,18 +157,11 @@ class UniversalScaffold extends StatefulWidget { /// /// If [breakpoint] is specified, the UI will change to a mobile-oriented UI when the window size becomes smaller than the breakpoint. At that time, [sideBar] will be placed on [drawer], and the width will be maximized. /// - /// If [sideBarWidth] is specified, you can specify the width of [sideBar]. - /// /// サイドバーを作成します。 /// /// [breakpoint]を指定するとそのブレークポイント以下のウインドウサイズになると、モバイル向けのUIに変化します。その際[sideBar]は[drawer]に配置されるようになり、横幅は最大化されます。 final PreferredSizeWidget? sideBar; - /// If [sideBar] is specified, you can specify the width of [sideBar]. - /// - /// [sideBar]が指定されている場合、[sideBar]の横幅を指定できます。 - final double sideBarWidth; - /// The loading widget will now be displayed until the specified [Future] is completed. /// /// これで指定した[Future]が完了するまでローディングウィジェットを表示します。 @@ -526,14 +518,16 @@ class _UniversalScaffoldState extends State { if (widget.drawer != null || widget.sideBar == null) { return widget.drawer; } - final shouldShowSideBar = - widget.breakpoint?.shouldShowSideBar(context) ?? true; + final universalScope = + MasamuneAdapterScope.of(context); + final breakpoint = universalScope?.defaultBreakpoint ?? widget.breakpoint; + final shouldShowSideBar = breakpoint?.shouldShowSideBar(context) ?? true; if (shouldShowSideBar) { return null; } return Drawer( width: widget.sideBar!.preferredSize.width, - child: widget.sideBar!, + child: widget.sideBar, ); } @@ -541,8 +535,10 @@ class _UniversalScaffoldState extends State { if (widget.sideBar == null) { return body; } - final shouldShowSideBar = - widget.breakpoint?.shouldShowSideBar(context) ?? true; + final universalScope = + MasamuneAdapterScope.of(context); + final breakpoint = universalScope?.defaultBreakpoint ?? widget.breakpoint; + final shouldShowSideBar = breakpoint?.shouldShowSideBar(context) ?? true; if (!shouldShowSideBar) { return body; } diff --git a/packages/masamune/lib/universal/universal_side_bar.dart b/packages/masamune/lib/universal/universal_side_bar.dart index 87286c28c..244806a91 100644 --- a/packages/masamune/lib/universal/universal_side_bar.dart +++ b/packages/masamune/lib/universal/universal_side_bar.dart @@ -23,14 +23,20 @@ class UniversalSideBar extends StatelessWidget with PreferredSizeWidget { /// [UniversalScaffold]の[UniversalScaffold.sidebar]に設定してください。 /// /// [breakpoint]を設定すると、その[ResponsiveBreakpoint]の横幅に合わせてサイドバーのパディングが切り替わります。 - const UniversalSideBar({ + UniversalSideBar({ super.key, this.breakpoint, this.decoration, this.padding, - this.child, + required this.children, this.paddingWhenNotFullWidth, - }) : preferredSize = const Size.fromWidth(kSideBarWidth); + this.width = kSideBarWidth, + }) : preferredSize = Size.fromWidth(width); + + /// Width of sidebar. + /// + /// サイドバーの横幅。 + final double width; /// Decoration] in the sidebar. /// @@ -45,7 +51,7 @@ class UniversalSideBar extends StatelessWidget with PreferredSizeWidget { /// Widget for the contents of the sidebar. /// /// サイドバーの中身のウィジェット。 - final Widget? child; + final List children; /// Normal padding. /// @@ -70,7 +76,13 @@ class UniversalSideBar extends StatelessWidget with PreferredSizeWidget { : (paddingWhenNotFullWidth ?? universal?.defaultSidebarPaddingWhenNotFullWidth)), decoration: decoration, - child: child, + child: ListView.builder( + primary: false, + itemCount: children.length, + itemBuilder: (context, index) { + return children[index]; + }, + ), ); } diff --git a/packages/masamune/pubspec.lock b/packages/masamune/pubspec.lock index f9cd2bd00..f3c0d1efe 100644 --- a/packages/masamune/pubspec.lock +++ b/packages/masamune/pubspec.lock @@ -308,10 +308,10 @@ packages: dependency: "direct main" description: name: katana_model - sha256: "724abf5fd0b119848255dbe9ae9b91ab133ce13df1105e0d6926e522670dfe4c" + sha256: "4f62900759b58c77bc8a8d33c65d6c9be35e97f7c9b152db016d55e0ebba9673" url: "https://pub.dev" source: hosted - version: "1.5.13" + version: "1.5.14" katana_prefs: dependency: "direct main" description: @@ -332,10 +332,10 @@ packages: dependency: "direct main" description: name: katana_responsive - sha256: "11803aa79a320f3946b8ae69dae657079e6f23449303cda8874d155bbd7ccaf3" + sha256: "961ebe2d9153264c57cd3ed3a88776aab8129711427b01bb53c1841cea76df76" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.3.3" katana_router: dependency: "direct main" description: