diff --git a/dev/integration_tests/new_gallery/README.md b/dev/integration_tests/new_gallery/README.md
new file mode 100644
index 0000000000..f093ebbc12
--- /dev/null
+++ b/dev/integration_tests/new_gallery/README.md
@@ -0,0 +1,29 @@
+# Flutter Gallery
+
+**NOTE**: The Flutter Gallery is now deprecated, and no longer being active maintained.
+
+Flutter Gallery was a resource to help developers evaluate and use Flutter.
+It is now being used primarily for testing. For posterity, the web version
+remains [hosted here](https://flutter-gallery-archive.web.app).
+
+We recommend Flutter developers check out the following resources:
+
+* **Wonderous**
+([web demo](https://wonderous.app/web/),
+[App Store](https://apps.apple.com/us/app/wonderous/id1612491897),
+[Google Play](https://play.google.com/store/apps/details?id=com.gskinner.flutter.wonders),
+[source code](https://github.com/gskinnerTeam/flutter-wonderous-app)):
+A Flutter app that showcases Flutter's support for elegant design and rich animations.
+
+* **Material 3 Demo**
+([web demo](https://flutter.github.io/samples/web/material_3_demo/),
+[source code](https://github.com/flutter/samples/tree/main/material_3_demo)):
+A Flutter app that showcases Material 3 features in the Flutter Material library.
+
+* **Flutter Samples**
+([samples](https://flutter.github.io/samples), [source code](https://github.com/flutter/samples)):
+A collection of open source samples that illustrate best practices for Flutter.
+
+* **Widget catalogs**
+([Material](https://docs.flutter.dev/ui/widgets/material), [Cupertino](https://docs.flutter.dev/ui/widgets/cupertino)):
+Catalogs for Material, Cupertino, and other widgets available for use in UI.
diff --git a/dev/integration_tests/new_gallery/lib/codeviewer/code_displayer.dart b/dev/integration_tests/new_gallery/lib/codeviewer/code_displayer.dart
new file mode 100644
index 0000000000..fcfe2b121d
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/codeviewer/code_displayer.dart
@@ -0,0 +1,7 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+
+typedef CodeDisplayer = TextSpan Function(BuildContext context);
diff --git a/dev/integration_tests/new_gallery/lib/codeviewer/code_style.dart b/dev/integration_tests/new_gallery/lib/codeviewer/code_style.dart
new file mode 100644
index 0000000000..8ca8d175b3
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/codeviewer/code_style.dart
@@ -0,0 +1,44 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+
+class CodeStyle extends InheritedWidget {
+ const CodeStyle({
+ super.key,
+ this.baseStyle,
+ this.numberStyle,
+ this.commentStyle,
+ this.keywordStyle,
+ this.stringStyle,
+ this.punctuationStyle,
+ this.classStyle,
+ this.constantStyle,
+ required super.child,
+ });
+
+ final TextStyle? baseStyle;
+ final TextStyle? numberStyle;
+ final TextStyle? commentStyle;
+ final TextStyle? keywordStyle;
+ final TextStyle? stringStyle;
+ final TextStyle? punctuationStyle;
+ final TextStyle? classStyle;
+ final TextStyle? constantStyle;
+
+ static CodeStyle of(BuildContext context) {
+ return context.dependOnInheritedWidgetOfExactType()!;
+ }
+
+ @override
+ bool updateShouldNotify(CodeStyle oldWidget) =>
+ oldWidget.baseStyle != baseStyle ||
+ oldWidget.numberStyle != numberStyle ||
+ oldWidget.commentStyle != commentStyle ||
+ oldWidget.keywordStyle != keywordStyle ||
+ oldWidget.stringStyle != stringStyle ||
+ oldWidget.punctuationStyle != punctuationStyle ||
+ oldWidget.classStyle != classStyle ||
+ oldWidget.constantStyle != constantStyle;
+}
diff --git a/dev/integration_tests/new_gallery/lib/constants.dart b/dev/integration_tests/new_gallery/lib/constants.dart
new file mode 100644
index 0000000000..f9d1e1cde2
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/constants.dart
@@ -0,0 +1,53 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Only put constants shared between files here.
+
+import 'dart:typed_data';
+
+// Height of the 'Gallery' header
+const double galleryHeaderHeight = 64;
+
+// The font size delta for headline4 font.
+const double desktopDisplay1FontDelta = 16;
+
+// The width of the settingsDesktop.
+const double desktopSettingsWidth = 520;
+
+// Sentinel value for the system text scale factor option.
+const double systemTextScaleFactorOption = -1;
+
+// The splash page animation duration.
+const Duration splashPageAnimationDuration = Duration(milliseconds: 300);
+
+// Half the splash page animation duration.
+const Duration halfSplashPageAnimationDuration = Duration(milliseconds: 150);
+
+// Duration for settings panel to open on mobile.
+const Duration settingsPanelMobileAnimationDuration =
+ Duration(milliseconds: 200);
+
+// Duration for settings panel to open on desktop.
+const Duration settingsPanelDesktopAnimationDuration =
+ Duration(milliseconds: 600);
+
+// Duration for home page elements to fade in.
+const Duration entranceAnimationDuration = Duration(milliseconds: 200);
+
+// The desktop top padding for a page's first header (e.g. Gallery, Settings)
+const double firstHeaderDesktopTopPadding = 5.0;
+
+// A transparent image used to avoid loading images when they are not needed.
+final Uint8List kTransparentImage = Uint8List.fromList([
+ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49,
+ 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06,
+ 0x00, 0x00, 0x00, 0x1F, 0x15, 0xC4, 0x89, 0x00, 0x00, 0x00, 0x06, 0x62, 0x4B,
+ 0x47, 0x44, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xA0, 0xBD, 0xA7, 0x93, 0x00,
+ 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0B, 0x13, 0x00, 0x00,
+ 0x0B, 0x13, 0x01, 0x00, 0x9A, 0x9C, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49,
+ 0x4D, 0x45, 0x07, 0xE6, 0x03, 0x10, 0x17, 0x07, 0x1D, 0x2E, 0x5E, 0x30, 0x9B,
+ 0x00, 0x00, 0x00, 0x0B, 0x49, 0x44, 0x41, 0x54, 0x08, 0xD7, 0x63, 0x60, 0x00,
+ 0x02, 0x00, 0x00, 0x05, 0x00, 0x01, 0xE2, 0x26, 0x05, 0x9B, 0x00, 0x00, 0x00,
+ 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82,
+]);
diff --git a/dev/integration_tests/new_gallery/lib/data/demos.dart b/dev/integration_tests/new_gallery/lib/data/demos.dart
new file mode 100644
index 0000000000..48d8049ae2
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/data/demos.dart
@@ -0,0 +1,1323 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:collection';
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+
+import '../codeviewer/code_displayer.dart';
+import '../deferred_widget.dart';
+import '../demos/cupertino/cupertino_demos.dart'
+ deferred as cupertino_demos;
+import '../demos/cupertino/demo_types.dart';
+import '../demos/material/material_demo_types.dart';
+import '../demos/material/material_demos.dart'
+ deferred as material_demos;
+import '../demos/reference/colors_demo.dart'
+ deferred as colors_demo;
+import '../demos/reference/motion_demo_container_transition.dart'
+ deferred as motion_demo_container;
+import '../demos/reference/motion_demo_fade_scale_transition.dart';
+import '../demos/reference/motion_demo_fade_through_transition.dart';
+import '../demos/reference/motion_demo_shared_x_axis_transition.dart';
+import '../demos/reference/motion_demo_shared_y_axis_transition.dart';
+import '../demos/reference/motion_demo_shared_z_axis_transition.dart';
+import '../demos/reference/transformations_demo.dart'
+ deferred as transformations_demo;
+import '../demos/reference/two_pane_demo.dart'
+ deferred as twopane_demo;
+import '../demos/reference/typography_demo.dart'
+ deferred as typography;
+import '../gallery_localizations.dart';
+import '../gallery_localizations_en.dart';
+import 'icons.dart';
+
+const String _docsBaseUrl = 'https://api.flutter.dev/flutter';
+const String _docsAnimationsUrl =
+ 'https://pub.dev/documentation/animations/latest/animations';
+
+enum GalleryDemoCategory {
+ study,
+ material,
+ cupertino,
+ other;
+
+ @override
+ String toString() {
+ return name.toUpperCase();
+ }
+
+ String? displayTitle(GalleryLocalizations localizations) {
+ switch (this) {
+ case GalleryDemoCategory.other:
+ return localizations.homeCategoryReference;
+ case GalleryDemoCategory.material:
+ case GalleryDemoCategory.cupertino:
+ return toString();
+ case GalleryDemoCategory.study:
+ }
+ return null;
+ }
+}
+
+class GalleryDemo {
+ const GalleryDemo({
+ required this.title,
+ required this.category,
+ required this.subtitle,
+ // This parameter is required for studies.
+ this.studyId,
+ // Parameters below are required for non-study demos.
+ this.slug,
+ this.icon,
+ this.configurations = const [],
+ }) : assert(category == GalleryDemoCategory.study ||
+ (slug != null && icon != null)),
+ assert(slug != null || studyId != null);
+
+ final String title;
+ final GalleryDemoCategory category;
+ final String subtitle;
+ final String? studyId;
+ final String? slug;
+ final IconData? icon;
+ final List configurations;
+
+ String get describe => '${slug ?? studyId}@${category.name}';
+}
+
+TextSpan noOpCodeDisplayer(BuildContext context) {
+ return const TextSpan(text: '');
+}
+
+class GalleryDemoConfiguration {
+ const GalleryDemoConfiguration({
+ required this.title,
+ required this.description,
+ required this.documentationUrl,
+ required this.buildRoute,
+ this.code = noOpCodeDisplayer,
+ });
+
+ final String title;
+ final String description;
+ final String documentationUrl;
+ final WidgetBuilder buildRoute;
+ final CodeDisplayer code;
+}
+
+/// Awaits all deferred libraries for tests.
+Future pumpDeferredLibraries() {
+ final List> futures = >[
+ DeferredWidget.preload(cupertino_demos.loadLibrary),
+ DeferredWidget.preload(material_demos.loadLibrary),
+ DeferredWidget.preload(motion_demo_container.loadLibrary),
+ DeferredWidget.preload(colors_demo.loadLibrary),
+ DeferredWidget.preload(transformations_demo.loadLibrary),
+ DeferredWidget.preload(typography.loadLibrary),
+ ];
+ return Future.wait(futures);
+}
+
+class Demos {
+ static Map asSlugToDemoMap(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ return LinkedHashMap.fromIterable(
+ all(localizations),
+ // ignore: avoid_dynamic_calls
+ key: (dynamic demo) => demo.slug as String?,
+ );
+ }
+
+ static List all(GalleryLocalizations localizations) =>
+ studies(localizations).values.toList() +
+ materialDemos(localizations) +
+ cupertinoDemos(localizations) +
+ otherDemos(localizations);
+
+ static List allDescriptions() =>
+ all(GalleryLocalizationsEn()).map((GalleryDemo demo) => demo.describe).toList();
+
+ static Map studies(GalleryLocalizations localizations) {
+ return {
+ 'shrine': GalleryDemo(
+ title: 'Shrine',
+ subtitle: localizations.shrineDescription,
+ category: GalleryDemoCategory.study,
+ studyId: 'shrine',
+ ),
+ 'rally': GalleryDemo(
+ title: 'Rally',
+ subtitle: localizations.rallyDescription,
+ category: GalleryDemoCategory.study,
+ studyId: 'rally',
+ ),
+ 'crane': GalleryDemo(
+ title: 'Crane',
+ subtitle: localizations.craneDescription,
+ category: GalleryDemoCategory.study,
+ studyId: 'crane',
+ ),
+ 'fortnightly': GalleryDemo(
+ title: 'Fortnightly',
+ subtitle: localizations.fortnightlyDescription,
+ category: GalleryDemoCategory.study,
+ studyId: 'fortnightly',
+ ),
+ 'reply': GalleryDemo(
+ title: 'Reply',
+ subtitle: localizations.replyDescription,
+ category: GalleryDemoCategory.study,
+ studyId: 'reply',
+ ),
+ 'starterApp': GalleryDemo(
+ title: localizations.starterAppTitle,
+ subtitle: localizations.starterAppDescription,
+ category: GalleryDemoCategory.study,
+ studyId: 'starter',
+ ),
+ };
+ }
+
+ static List materialDemos(GalleryLocalizations localizations) {
+ final LibraryLoader materialDemosLibrary = material_demos.loadLibrary;
+ return [
+ GalleryDemo(
+ title: localizations.demoAppBarTitle,
+ icon: GalleryIcons.appbar,
+ slug: 'app-bar',
+ subtitle: localizations.demoAppBarSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoAppBarTitle,
+ description: localizations.demoAppBarDescription,
+ documentationUrl: '$_docsBaseUrl/material/AppBar-class.html',
+ buildRoute: (_) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.AppBarDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoBannerTitle,
+ icon: GalleryIcons.listsLeaveBehind,
+ slug: 'banner',
+ subtitle: localizations.demoBannerSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoBannerTitle,
+ description: localizations.demoBannerDescription,
+ documentationUrl:
+ '$_docsBaseUrl/material/MaterialBanner-class.html',
+ buildRoute: (_) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.BannerDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoBottomAppBarTitle,
+ icon: GalleryIcons.bottomAppBar,
+ slug: 'bottom-app-bar',
+ subtitle: localizations.demoBottomAppBarSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoBottomAppBarTitle,
+ description: localizations.demoBottomAppBarDescription,
+ documentationUrl: '$_docsBaseUrl/material/BottomAppBar-class.html',
+ buildRoute: (_) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.BottomAppBarDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoBottomNavigationTitle,
+ icon: GalleryIcons.bottomNavigation,
+ slug: 'bottom-navigation',
+ subtitle: localizations.demoBottomNavigationSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoBottomNavigationPersistentLabels,
+ description: localizations.demoBottomNavigationDescription,
+ documentationUrl:
+ '$_docsBaseUrl/material/BottomNavigationBar-class.html',
+ buildRoute: (_) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.BottomNavigationDemo(
+ type: BottomNavigationDemoType.withLabels,
+ restorationId: 'bottom_navigation_labels_demo',
+ )),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoBottomNavigationSelectedLabel,
+ description: localizations.demoBottomNavigationDescription,
+ documentationUrl:
+ '$_docsBaseUrl/material/BottomNavigationBar-class.html',
+ buildRoute: (_) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.BottomNavigationDemo(
+ type: BottomNavigationDemoType.withoutLabels,
+ restorationId: 'bottom_navigation_without_labels_demo',
+ )),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoBottomSheetTitle,
+ icon: GalleryIcons.bottomSheets,
+ slug: 'bottom-sheet',
+ subtitle: localizations.demoBottomSheetSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoBottomSheetPersistentTitle,
+ description: localizations.demoBottomSheetPersistentDescription,
+ documentationUrl: '$_docsBaseUrl/material/BottomSheet-class.html',
+ buildRoute: (_) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.BottomSheetDemo(
+ type: BottomSheetDemoType.persistent,
+ )),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoBottomSheetModalTitle,
+ description: localizations.demoBottomSheetModalDescription,
+ documentationUrl: '$_docsBaseUrl/material/BottomSheet-class.html',
+ buildRoute: (_) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.BottomSheetDemo(
+ type: BottomSheetDemoType.modal,
+ )),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoButtonTitle,
+ icon: GalleryIcons.genericButtons,
+ slug: 'button',
+ subtitle: localizations.demoButtonSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoTextButtonTitle,
+ description: localizations.demoTextButtonDescription,
+ documentationUrl: '$_docsBaseUrl/material/TextButton-class.html',
+ buildRoute: (_) => DeferredWidget(materialDemosLibrary,
+ () => material_demos.ButtonDemo(type: ButtonDemoType.text)),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoElevatedButtonTitle,
+ description: localizations.demoElevatedButtonDescription,
+ documentationUrl:
+ '$_docsBaseUrl/material/ElevatedButton-class.html',
+ buildRoute: (_) => DeferredWidget(materialDemosLibrary,
+ () => material_demos.ButtonDemo(type: ButtonDemoType.elevated)),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoOutlinedButtonTitle,
+ description: localizations.demoOutlinedButtonDescription,
+ documentationUrl:
+ '$_docsBaseUrl/material/OutlinedButton-class.html',
+ buildRoute: (_) => DeferredWidget(materialDemosLibrary,
+ () => material_demos.ButtonDemo(type: ButtonDemoType.outlined)),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoToggleButtonTitle,
+ description: localizations.demoToggleButtonDescription,
+ documentationUrl: '$_docsBaseUrl/material/ToggleButtons-class.html',
+ buildRoute: (_) => DeferredWidget(materialDemosLibrary,
+ () => material_demos.ButtonDemo(type: ButtonDemoType.toggle)),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoFloatingButtonTitle,
+ description: localizations.demoFloatingButtonDescription,
+ documentationUrl:
+ '$_docsBaseUrl/material/FloatingActionButton-class.html',
+ buildRoute: (_) => DeferredWidget(materialDemosLibrary,
+ () => material_demos.ButtonDemo(type: ButtonDemoType.floating)),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoCardTitle,
+ icon: GalleryIcons.cards,
+ slug: 'card',
+ subtitle: localizations.demoCardSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoCardTitle,
+ description: localizations.demoCardDescription,
+ documentationUrl: '$_docsBaseUrl/material/Card-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.CardsDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoChipTitle,
+ icon: GalleryIcons.chips,
+ slug: 'chip',
+ subtitle: localizations.demoChipSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoActionChipTitle,
+ description: localizations.demoActionChipDescription,
+ documentationUrl: '$_docsBaseUrl/material/ActionChip-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(materialDemosLibrary,
+ () => material_demos.ChipDemo(type: ChipDemoType.action)),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoChoiceChipTitle,
+ description: localizations.demoChoiceChipDescription,
+ documentationUrl: '$_docsBaseUrl/material/ChoiceChip-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(materialDemosLibrary,
+ () => material_demos.ChipDemo(type: ChipDemoType.choice)),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoFilterChipTitle,
+ description: localizations.demoFilterChipDescription,
+ documentationUrl: '$_docsBaseUrl/material/FilterChip-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(materialDemosLibrary,
+ () => material_demos.ChipDemo(type: ChipDemoType.filter)),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoInputChipTitle,
+ description: localizations.demoInputChipDescription,
+ documentationUrl: '$_docsBaseUrl/material/InputChip-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(materialDemosLibrary,
+ () => material_demos.ChipDemo(type: ChipDemoType.input)),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoDataTableTitle,
+ icon: GalleryIcons.dataTable,
+ slug: 'data-table',
+ subtitle: localizations.demoDataTableSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoDataTableTitle,
+ description: localizations.demoDataTableDescription,
+ documentationUrl: '$_docsBaseUrl/material/DataTable-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.DataTableDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoDialogTitle,
+ icon: GalleryIcons.dialogs,
+ slug: 'dialog',
+ subtitle: localizations.demoDialogSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoAlertDialogTitle,
+ description: localizations.demoAlertDialogDescription,
+ documentationUrl: '$_docsBaseUrl/material/AlertDialog-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(materialDemosLibrary,
+ () => material_demos.DialogDemo(type: DialogDemoType.alert)),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoAlertTitleDialogTitle,
+ description: localizations.demoAlertDialogDescription,
+ documentationUrl: '$_docsBaseUrl/material/AlertDialog-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () =>
+ material_demos.DialogDemo(type: DialogDemoType.alertTitle)),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoSimpleDialogTitle,
+ description: localizations.demoSimpleDialogDescription,
+ documentationUrl: '$_docsBaseUrl/material/SimpleDialog-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(materialDemosLibrary,
+ () => material_demos.DialogDemo(type: DialogDemoType.simple)),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoFullscreenDialogTitle,
+ description: localizations.demoFullscreenDialogDescription,
+ documentationUrl:
+ '$_docsBaseUrl/widgets/PageRoute/fullscreenDialog.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () =>
+ material_demos.DialogDemo(type: DialogDemoType.fullscreen)),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoDividerTitle,
+ icon: GalleryIcons.divider,
+ slug: 'divider',
+ subtitle: localizations.demoDividerSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoDividerTitle,
+ description: localizations.demoDividerDescription,
+ documentationUrl: '$_docsBaseUrl/material/Divider-class.html',
+ buildRoute: (_) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.DividerDemo(
+ type: DividerDemoType.horizontal)),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoVerticalDividerTitle,
+ description: localizations.demoDividerDescription,
+ documentationUrl:
+ '$_docsBaseUrl/material/VerticalDivider-class.html',
+ buildRoute: (_) => DeferredWidget(
+ materialDemosLibrary,
+ () =>
+ material_demos.DividerDemo(type: DividerDemoType.vertical)),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoGridListsTitle,
+ icon: GalleryIcons.gridOn,
+ slug: 'grid-lists',
+ subtitle: localizations.demoGridListsSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoGridListsImageOnlyTitle,
+ description: localizations.demoGridListsDescription,
+ documentationUrl: '$_docsBaseUrl/widgets/GridView-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.GridListDemo(
+ type: GridListDemoType.imageOnly)),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoGridListsHeaderTitle,
+ description: localizations.demoGridListsDescription,
+ documentationUrl: '$_docsBaseUrl/widgets/GridView-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () =>
+ material_demos.GridListDemo(type: GridListDemoType.header)),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoGridListsFooterTitle,
+ description: localizations.demoGridListsDescription,
+ documentationUrl: '$_docsBaseUrl/widgets/GridView-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () =>
+ material_demos.GridListDemo(type: GridListDemoType.footer)),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoListsTitle,
+ icon: GalleryIcons.listAlt,
+ slug: 'lists',
+ subtitle: localizations.demoListsSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoOneLineListsTitle,
+ description: localizations.demoListsDescription,
+ documentationUrl: '$_docsBaseUrl/material/ListTile-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(materialDemosLibrary,
+ () => material_demos.ListDemo(type: ListDemoType.oneLine)),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoTwoLineListsTitle,
+ description: localizations.demoListsDescription,
+ documentationUrl: '$_docsBaseUrl/material/ListTile-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(materialDemosLibrary,
+ () => material_demos.ListDemo(type: ListDemoType.twoLine)),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoMenuTitle,
+ icon: GalleryIcons.moreVert,
+ slug: 'menu',
+ subtitle: localizations.demoMenuSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoContextMenuTitle,
+ description: localizations.demoMenuDescription,
+ documentationUrl: '$_docsBaseUrl/material/PopupMenuItem-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.MenuDemo(type: MenuDemoType.contextMenu),
+ ),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoSectionedMenuTitle,
+ description: localizations.demoMenuDescription,
+ documentationUrl: '$_docsBaseUrl/material/PopupMenuItem-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.MenuDemo(type: MenuDemoType.sectionedMenu),
+ ),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoChecklistMenuTitle,
+ description: localizations.demoMenuDescription,
+ documentationUrl:
+ '$_docsBaseUrl/material/CheckedPopupMenuItem-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.MenuDemo(type: MenuDemoType.checklistMenu),
+ ),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoSimpleMenuTitle,
+ description: localizations.demoMenuDescription,
+ documentationUrl: '$_docsBaseUrl/material/PopupMenuItem-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.MenuDemo(type: MenuDemoType.simpleMenu),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoNavigationDrawerTitle,
+ icon: GalleryIcons.menu,
+ slug: 'nav_drawer',
+ subtitle: localizations.demoNavigationDrawerSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoNavigationDrawerTitle,
+ description: localizations.demoNavigationDrawerDescription,
+ documentationUrl: '$_docsBaseUrl/material/Drawer-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.NavDrawerDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoNavigationRailTitle,
+ icon: GalleryIcons.navigationRail,
+ slug: 'nav_rail',
+ subtitle: localizations.demoNavigationRailSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoNavigationRailTitle,
+ description: localizations.demoNavigationRailDescription,
+ documentationUrl:
+ '$_docsBaseUrl/material/NavigationRail-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.NavRailDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoPickersTitle,
+ icon: GalleryIcons.event,
+ slug: 'pickers',
+ subtitle: localizations.demoPickersSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoDatePickerTitle,
+ description: localizations.demoDatePickerDescription,
+ documentationUrl: '$_docsBaseUrl/material/showDatePicker.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.PickerDemo(type: PickerDemoType.date),
+ ),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoTimePickerTitle,
+ description: localizations.demoTimePickerDescription,
+ documentationUrl: '$_docsBaseUrl/material/showTimePicker.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.PickerDemo(type: PickerDemoType.time),
+ ),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoDateRangePickerTitle,
+ description: localizations.demoDateRangePickerDescription,
+ documentationUrl: '$_docsBaseUrl/material/showDateRangePicker.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.PickerDemo(type: PickerDemoType.range),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoProgressIndicatorTitle,
+ icon: GalleryIcons.progressActivity,
+ slug: 'progress-indicator',
+ subtitle: localizations.demoProgressIndicatorSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoCircularProgressIndicatorTitle,
+ description: localizations.demoCircularProgressIndicatorDescription,
+ documentationUrl:
+ '$_docsBaseUrl/material/CircularProgressIndicator-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.ProgressIndicatorDemo(
+ type: ProgressIndicatorDemoType.circular,
+ ),
+ ),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoLinearProgressIndicatorTitle,
+ description: localizations.demoLinearProgressIndicatorDescription,
+ documentationUrl:
+ '$_docsBaseUrl/material/LinearProgressIndicator-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.ProgressIndicatorDemo(
+ type: ProgressIndicatorDemoType.linear,
+ ),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoSelectionControlsTitle,
+ icon: GalleryIcons.checkBox,
+ slug: 'selection-controls',
+ subtitle: localizations.demoSelectionControlsSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoSelectionControlsCheckboxTitle,
+ description: localizations.demoSelectionControlsCheckboxDescription,
+ documentationUrl: '$_docsBaseUrl/material/Checkbox-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.SelectionControlsDemo(
+ type: SelectionControlsDemoType.checkbox,
+ ),
+ ),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoSelectionControlsRadioTitle,
+ description: localizations.demoSelectionControlsRadioDescription,
+ documentationUrl: '$_docsBaseUrl/material/Radio-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.SelectionControlsDemo(
+ type: SelectionControlsDemoType.radio,
+ ),
+ ),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoSelectionControlsSwitchTitle,
+ description: localizations.demoSelectionControlsSwitchDescription,
+ documentationUrl: '$_docsBaseUrl/material/Switch-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.SelectionControlsDemo(
+ type: SelectionControlsDemoType.switches,
+ ),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoSlidersTitle,
+ icon: GalleryIcons.sliders,
+ slug: 'sliders',
+ subtitle: localizations.demoSlidersSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoSlidersTitle,
+ description: localizations.demoSlidersDescription,
+ documentationUrl: '$_docsBaseUrl/material/Slider-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.SlidersDemo(type: SlidersDemoType.sliders),
+ ),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoRangeSlidersTitle,
+ description: localizations.demoRangeSlidersDescription,
+ documentationUrl: '$_docsBaseUrl/material/RangeSlider-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.SlidersDemo(
+ type: SlidersDemoType.rangeSliders),
+ ),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoCustomSlidersTitle,
+ description: localizations.demoCustomSlidersDescription,
+ documentationUrl: '$_docsBaseUrl/material/SliderTheme-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.SlidersDemo(
+ type: SlidersDemoType.customSliders),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoSnackbarsTitle,
+ icon: GalleryIcons.snackbar,
+ slug: 'snackbars',
+ subtitle: localizations.demoSnackbarsSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoSnackbarsTitle,
+ description: localizations.demoSnackbarsDescription,
+ documentationUrl: '$_docsBaseUrl/material/SnackBar-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.SnackbarsDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoTabsTitle,
+ icon: GalleryIcons.tabs,
+ slug: 'tabs',
+ subtitle: localizations.demoTabsSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoTabsScrollingTitle,
+ description: localizations.demoTabsDescription,
+ documentationUrl: '$_docsBaseUrl/material/TabBar-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.TabsDemo(type: TabsDemoType.scrollable),
+ ),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoTabsNonScrollingTitle,
+ description: localizations.demoTabsDescription,
+ documentationUrl: '$_docsBaseUrl/material/TabBar-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.TabsDemo(type: TabsDemoType.nonScrollable),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoTextFieldTitle,
+ icon: GalleryIcons.textFieldsAlt,
+ slug: 'text-field',
+ subtitle: localizations.demoTextFieldSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoTextFieldTitle,
+ description: localizations.demoTextFieldDescription,
+ documentationUrl: '$_docsBaseUrl/material/TextField-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.TextFieldDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ GalleryDemo(
+ title: localizations.demoTooltipTitle,
+ icon: GalleryIcons.tooltip,
+ slug: 'tooltip',
+ subtitle: localizations.demoTooltipSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoTooltipTitle,
+ description: localizations.demoTooltipDescription,
+ documentationUrl: '$_docsBaseUrl/material/Tooltip-class.html',
+ buildRoute: (BuildContext context) => DeferredWidget(
+ materialDemosLibrary,
+ () => material_demos.TooltipDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.material,
+ ),
+ ];
+ }
+
+ static List cupertinoDemos(GalleryLocalizations localizations) {
+ final LibraryLoader cupertinoLoader = cupertino_demos.loadLibrary;
+ return [
+ GalleryDemo(
+ title: localizations.demoCupertinoActivityIndicatorTitle,
+ icon: GalleryIcons.cupertinoProgress,
+ slug: 'cupertino-activity-indicator',
+ subtitle: localizations.demoCupertinoActivityIndicatorSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoCupertinoActivityIndicatorTitle,
+ description:
+ localizations.demoCupertinoActivityIndicatorDescription,
+ documentationUrl:
+ '$_docsBaseUrl/cupertino/CupertinoActivityIndicator-class.html',
+ buildRoute: (_) => DeferredWidget(
+ cupertinoLoader,
+ () => cupertino_demos.CupertinoProgressIndicatorDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.cupertino,
+ ),
+ GalleryDemo(
+ title: localizations.demoCupertinoAlertsTitle,
+ icon: GalleryIcons.dialogs,
+ slug: 'cupertino-alerts',
+ subtitle: localizations.demoCupertinoAlertsSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoCupertinoAlertTitle,
+ description: localizations.demoCupertinoAlertDescription,
+ documentationUrl:
+ '$_docsBaseUrl/cupertino/CupertinoAlertDialog-class.html',
+ buildRoute: (_) => DeferredWidget(
+ cupertinoLoader,
+ () => cupertino_demos.CupertinoAlertDemo(
+ type: AlertDemoType.alert)),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoCupertinoAlertWithTitleTitle,
+ description: localizations.demoCupertinoAlertDescription,
+ documentationUrl:
+ '$_docsBaseUrl/cupertino/CupertinoAlertDialog-class.html',
+ buildRoute: (_) => DeferredWidget(
+ cupertinoLoader,
+ () => cupertino_demos.CupertinoAlertDemo(
+ type: AlertDemoType.alertTitle)),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoCupertinoAlertButtonsTitle,
+ description: localizations.demoCupertinoAlertDescription,
+ documentationUrl:
+ '$_docsBaseUrl/cupertino/CupertinoAlertDialog-class.html',
+ buildRoute: (_) => DeferredWidget(
+ cupertinoLoader,
+ () => cupertino_demos.CupertinoAlertDemo(
+ type: AlertDemoType.alertButtons)),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoCupertinoAlertButtonsOnlyTitle,
+ description: localizations.demoCupertinoAlertDescription,
+ documentationUrl:
+ '$_docsBaseUrl/cupertino/CupertinoAlertDialog-class.html',
+ buildRoute: (_) => DeferredWidget(
+ cupertinoLoader,
+ () => cupertino_demos.CupertinoAlertDemo(
+ type: AlertDemoType.alertButtonsOnly)),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoCupertinoActionSheetTitle,
+ description: localizations.demoCupertinoActionSheetDescription,
+ documentationUrl:
+ '$_docsBaseUrl/cupertino/CupertinoActionSheet-class.html',
+ buildRoute: (_) => DeferredWidget(
+ cupertinoLoader,
+ () => cupertino_demos.CupertinoAlertDemo(
+ type: AlertDemoType.actionSheet)),
+ ),
+ ],
+ category: GalleryDemoCategory.cupertino,
+ ),
+ GalleryDemo(
+ title: localizations.demoCupertinoButtonsTitle,
+ icon: GalleryIcons.genericButtons,
+ slug: 'cupertino-buttons',
+ subtitle: localizations.demoCupertinoButtonsSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoCupertinoButtonsTitle,
+ description: localizations.demoCupertinoButtonsDescription,
+ documentationUrl:
+ '$_docsBaseUrl/cupertino/CupertinoButton-class.html',
+ buildRoute: (_) => DeferredWidget(
+ cupertinoLoader,
+ () => cupertino_demos.CupertinoButtonDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.cupertino,
+ ),
+ GalleryDemo(
+ title: localizations.demoCupertinoContextMenuTitle,
+ icon: GalleryIcons.moreVert,
+ slug: 'cupertino-context-menu',
+ subtitle: localizations.demoCupertinoContextMenuSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoCupertinoContextMenuTitle,
+ description: localizations.demoCupertinoContextMenuDescription,
+ documentationUrl:
+ '$_docsBaseUrl/cupertino/CupertinoContextMenu-class.html',
+ buildRoute: (_) => DeferredWidget(
+ cupertinoLoader,
+ () => cupertino_demos.CupertinoContextMenuDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.cupertino,
+ ),
+ GalleryDemo(
+ title: localizations.demoCupertinoNavigationBarTitle,
+ icon: GalleryIcons.bottomSheetPersistent,
+ slug: 'cupertino-navigation-bar',
+ subtitle: localizations.demoCupertinoNavigationBarSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoCupertinoNavigationBarTitle,
+ description: localizations.demoCupertinoNavigationBarDescription,
+ documentationUrl:
+ '$_docsBaseUrl/cupertino/CupertinoNavigationBar-class.html',
+ buildRoute: (_) => DeferredWidget(
+ cupertinoLoader,
+ () => cupertino_demos.CupertinoNavigationBarDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.cupertino,
+ ),
+ GalleryDemo(
+ title: localizations.demoCupertinoPickerTitle,
+ icon: GalleryIcons.listAlt,
+ slug: 'cupertino-picker',
+ subtitle: localizations.demoCupertinoPickerSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoCupertinoPickerTitle,
+ description: localizations.demoCupertinoPickerDescription,
+ documentationUrl:
+ '$_docsBaseUrl/cupertino/CupertinoDatePicker-class.html',
+ buildRoute: (_) => DeferredWidget(
+ cupertinoLoader,
+ // ignore: prefer_const_constructors
+ () => cupertino_demos.CupertinoPickerDemo()),
+ ),
+ ],
+ category: GalleryDemoCategory.cupertino,
+ ),
+ GalleryDemo(
+ title: localizations.demoCupertinoScrollbarTitle,
+ icon: GalleryIcons.listAlt,
+ slug: 'cupertino-scrollbar',
+ subtitle: localizations.demoCupertinoScrollbarSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoCupertinoScrollbarTitle,
+ description: localizations.demoCupertinoScrollbarDescription,
+ documentationUrl:
+ '$_docsBaseUrl/cupertino/CupertinoScrollbar-class.html',
+ buildRoute: (_) => DeferredWidget(
+ cupertinoLoader,
+ // ignore: prefer_const_constructors
+ () => cupertino_demos.CupertinoScrollbarDemo()),
+ ),
+ ],
+ category: GalleryDemoCategory.cupertino,
+ ),
+ GalleryDemo(
+ title: localizations.demoCupertinoSegmentedControlTitle,
+ icon: GalleryIcons.tabs,
+ slug: 'cupertino-segmented-control',
+ subtitle: localizations.demoCupertinoSegmentedControlSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoCupertinoSegmentedControlTitle,
+ description: localizations.demoCupertinoSegmentedControlDescription,
+ documentationUrl:
+ '$_docsBaseUrl/cupertino/CupertinoSegmentedControl-class.html',
+ buildRoute: (_) => DeferredWidget(
+ cupertinoLoader,
+ () => cupertino_demos.CupertinoSegmentedControlDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.cupertino,
+ ),
+ GalleryDemo(
+ title: localizations.demoCupertinoSliderTitle,
+ icon: GalleryIcons.sliders,
+ slug: 'cupertino-slider',
+ subtitle: localizations.demoCupertinoSliderSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoCupertinoSliderTitle,
+ description: localizations.demoCupertinoSliderDescription,
+ documentationUrl:
+ '$_docsBaseUrl/cupertino/CupertinoSlider-class.html',
+ buildRoute: (_) => DeferredWidget(
+ cupertinoLoader,
+ () => cupertino_demos.CupertinoSliderDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.cupertino,
+ ),
+ GalleryDemo(
+ title: localizations.demoSelectionControlsSwitchTitle,
+ icon: GalleryIcons.cupertinoSwitch,
+ slug: 'cupertino-switch',
+ subtitle: localizations.demoCupertinoSwitchSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoSelectionControlsSwitchTitle,
+ description: localizations.demoCupertinoSwitchDescription,
+ documentationUrl:
+ '$_docsBaseUrl/cupertino/CupertinoSwitch-class.html',
+ buildRoute: (_) => DeferredWidget(
+ cupertinoLoader,
+ () => cupertino_demos.CupertinoSwitchDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.cupertino,
+ ),
+ GalleryDemo(
+ title: localizations.demoCupertinoTabBarTitle,
+ icon: GalleryIcons.bottomNavigation,
+ slug: 'cupertino-tab-bar',
+ subtitle: localizations.demoCupertinoTabBarSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoCupertinoTabBarTitle,
+ description: localizations.demoCupertinoTabBarDescription,
+ documentationUrl:
+ '$_docsBaseUrl/cupertino/CupertinoTabBar-class.html',
+ buildRoute: (_) => DeferredWidget(
+ cupertinoLoader,
+ () => cupertino_demos.CupertinoTabBarDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.cupertino,
+ ),
+ GalleryDemo(
+ title: localizations.demoCupertinoTextFieldTitle,
+ icon: GalleryIcons.textFieldsAlt,
+ slug: 'cupertino-text-field',
+ subtitle: localizations.demoCupertinoTextFieldSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoCupertinoTextFieldTitle,
+ description: localizations.demoCupertinoTextFieldDescription,
+ documentationUrl:
+ '$_docsBaseUrl/cupertino/CupertinoTextField-class.html',
+ buildRoute: (_) => DeferredWidget(
+ cupertinoLoader,
+ () => cupertino_demos.CupertinoTextFieldDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.cupertino,
+ ),
+ GalleryDemo(
+ title: localizations.demoCupertinoSearchTextFieldTitle,
+ icon: GalleryIcons.search,
+ slug: 'cupertino-search-text-field',
+ subtitle: localizations.demoCupertinoSearchTextFieldSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoCupertinoSearchTextFieldTitle,
+ description: localizations.demoCupertinoSearchTextFieldDescription,
+ documentationUrl:
+ '$_docsBaseUrl/cupertino/CupertinoSearchTextField-class.html',
+ buildRoute: (_) => DeferredWidget(
+ cupertinoLoader,
+ () => cupertino_demos.CupertinoSearchTextFieldDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.cupertino,
+ ),
+ ];
+ }
+
+ static List otherDemos(GalleryLocalizations localizations) {
+ return [
+ GalleryDemo(
+ title: localizations.demoTwoPaneTitle,
+ icon: GalleryIcons.bottomSheetPersistent,
+ slug: 'two-pane',
+ subtitle: localizations.demoTwoPaneSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoTwoPaneFoldableLabel,
+ description: localizations.demoTwoPaneFoldableDescription,
+ documentationUrl:
+ 'https://pub.dev/documentation/dual_screen/latest/dual_screen/TwoPane-class.html',
+ buildRoute: (_) => DeferredWidget(
+ twopane_demo.loadLibrary,
+ () => twopane_demo.TwoPaneDemo(
+ type: twopane_demo.TwoPaneDemoType.foldable,
+ restorationId: 'two_pane_foldable',
+ ),
+ ),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoTwoPaneTabletLabel,
+ description: localizations.demoTwoPaneTabletDescription,
+ documentationUrl:
+ 'https://pub.dev/documentation/dual_screen/latest/dual_screen/TwoPane-class.html',
+ buildRoute: (_) => DeferredWidget(
+ twopane_demo.loadLibrary,
+ () => twopane_demo.TwoPaneDemo(
+ type: twopane_demo.TwoPaneDemoType.tablet,
+ restorationId: 'two_pane_tablet',
+ ),
+ ),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoTwoPaneSmallScreenLabel,
+ description: localizations.demoTwoPaneSmallScreenDescription,
+ documentationUrl:
+ 'https://pub.dev/documentation/dual_screen/latest/dual_screen/TwoPane-class.html',
+ buildRoute: (_) => DeferredWidget(
+ twopane_demo.loadLibrary,
+ () => twopane_demo.TwoPaneDemo(
+ type: twopane_demo.TwoPaneDemoType.smallScreen,
+ restorationId: 'two_pane_single',
+ ),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.other,
+ ),
+ GalleryDemo(
+ title: localizations.demoMotionTitle,
+ icon: GalleryIcons.animation,
+ slug: 'motion',
+ subtitle: localizations.demoMotionSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoContainerTransformTitle,
+ description: localizations.demoContainerTransformDescription,
+ documentationUrl: '$_docsAnimationsUrl/OpenContainer-class.html',
+ buildRoute: (_) => DeferredWidget(
+ motion_demo_container.loadLibrary,
+ () => motion_demo_container.OpenContainerTransformDemo(),
+ ),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoSharedXAxisTitle,
+ description: localizations.demoSharedAxisDescription,
+ documentationUrl:
+ '$_docsAnimationsUrl/SharedAxisTransition-class.html',
+ buildRoute: (_) => const SharedXAxisTransitionDemo(),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoSharedYAxisTitle,
+ description: localizations.demoSharedAxisDescription,
+ documentationUrl:
+ '$_docsAnimationsUrl/SharedAxisTransition-class.html',
+ buildRoute: (_) => const SharedYAxisTransitionDemo(),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoSharedZAxisTitle,
+ description: localizations.demoSharedAxisDescription,
+ documentationUrl:
+ '$_docsAnimationsUrl/SharedAxisTransition-class.html',
+ buildRoute: (_) => const SharedZAxisTransitionDemo(),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoFadeThroughTitle,
+ description: localizations.demoFadeThroughDescription,
+ documentationUrl:
+ '$_docsAnimationsUrl/FadeThroughTransition-class.html',
+ buildRoute: (_) => const FadeThroughTransitionDemo(),
+ ),
+ GalleryDemoConfiguration(
+ title: localizations.demoFadeScaleTitle,
+ description: localizations.demoFadeScaleDescription,
+ documentationUrl:
+ '$_docsAnimationsUrl/FadeScaleTransition-class.html',
+ buildRoute: (_) => const FadeScaleTransitionDemo(),
+ ),
+ ],
+ category: GalleryDemoCategory.other,
+ ),
+ GalleryDemo(
+ title: localizations.demoColorsTitle,
+ icon: GalleryIcons.colors,
+ slug: 'colors',
+ subtitle: localizations.demoColorsSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoColorsTitle,
+ description: localizations.demoColorsDescription,
+ documentationUrl: '$_docsBaseUrl/material/MaterialColor-class.html',
+ buildRoute: (_) => DeferredWidget(
+ colors_demo.loadLibrary,
+ () => colors_demo.ColorsDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.other,
+ ),
+ GalleryDemo(
+ title: localizations.demoTypographyTitle,
+ icon: GalleryIcons.customTypography,
+ slug: 'typography',
+ subtitle: localizations.demoTypographySubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demoTypographyTitle,
+ description: localizations.demoTypographyDescription,
+ documentationUrl: '$_docsBaseUrl/material/TextTheme-class.html',
+ buildRoute: (_) => DeferredWidget(
+ typography.loadLibrary,
+ () => typography.TypographyDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.other,
+ ),
+ GalleryDemo(
+ title: localizations.demo2dTransformationsTitle,
+ icon: GalleryIcons.gridOn,
+ slug: '2d-transformations',
+ subtitle: localizations.demo2dTransformationsSubtitle,
+ configurations: [
+ GalleryDemoConfiguration(
+ title: localizations.demo2dTransformationsTitle,
+ description: localizations.demo2dTransformationsDescription,
+ documentationUrl:
+ '$_docsBaseUrl/widgets/GestureDetector-class.html',
+ buildRoute: (_) => DeferredWidget(
+ transformations_demo.loadLibrary,
+ () => transformations_demo.TransformationsDemo(),
+ ),
+ ),
+ ],
+ category: GalleryDemoCategory.other,
+ ),
+ ];
+ }
+}
diff --git a/dev/integration_tests/new_gallery/lib/data/gallery_options.dart b/dev/integration_tests/new_gallery/lib/data/gallery_options.dart
new file mode 100644
index 0000000000..b896e4dcc0
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/data/gallery_options.dart
@@ -0,0 +1,282 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart' show timeDilation;
+import 'package:flutter/services.dart' show SystemUiOverlayStyle;
+import '../constants.dart';
+
+enum CustomTextDirection {
+ localeBased,
+ ltr,
+ rtl,
+}
+
+// See http://en.wikipedia.org/wiki/Right-to-left
+const List rtlLanguages = [
+ 'ar', // Arabic
+ 'fa', // Farsi
+ 'he', // Hebrew
+ 'ps', // Pashto
+ 'ur', // Urdu
+];
+
+// Fake locale to represent the system Locale option.
+const Locale systemLocaleOption = Locale('system');
+
+Locale? _deviceLocale;
+
+Locale? get deviceLocale => _deviceLocale;
+
+set deviceLocale(Locale? locale) {
+ _deviceLocale ??= locale;
+}
+
+@immutable
+class GalleryOptions {
+ const GalleryOptions({
+ required this.themeMode,
+ required double? textScaleFactor,
+ required this.customTextDirection,
+ required Locale? locale,
+ required this.timeDilation,
+ required this.platform,
+ required this.isTestMode,
+ }) : _textScaleFactor = textScaleFactor ?? 1.0,
+ _locale = locale;
+
+ final ThemeMode themeMode;
+ final double _textScaleFactor;
+ final CustomTextDirection customTextDirection;
+ final Locale? _locale;
+ final double timeDilation;
+ final TargetPlatform? platform;
+ final bool isTestMode; // True for integration tests.
+
+ // We use a sentinel value to indicate the system text scale option. By
+ // default, return the actual text scale factor, otherwise return the
+ // sentinel value.
+ double textScaleFactor(BuildContext context, {bool useSentinel = false}) {
+ if (_textScaleFactor == systemTextScaleFactorOption) {
+ return useSentinel
+ ? systemTextScaleFactorOption
+ // ignore: deprecated_member_use
+ : MediaQuery.of(context).textScaleFactor;
+ } else {
+ return _textScaleFactor;
+ }
+ }
+
+ Locale? get locale => _locale ?? deviceLocale;
+
+ /// Returns a text direction based on the [CustomTextDirection] setting.
+ /// If it is based on locale and the locale cannot be determined, returns
+ /// null.
+ TextDirection? resolvedTextDirection() {
+ switch (customTextDirection) {
+ case CustomTextDirection.localeBased:
+ final String? language = locale?.languageCode.toLowerCase();
+ if (language == null) {
+ return null;
+ }
+ return rtlLanguages.contains(language)
+ ? TextDirection.rtl
+ : TextDirection.ltr;
+ case CustomTextDirection.rtl:
+ return TextDirection.rtl;
+ case CustomTextDirection.ltr:
+ return TextDirection.ltr;
+ }
+ }
+
+ /// Returns a [SystemUiOverlayStyle] based on the [ThemeMode] setting.
+ /// In other words, if the theme is dark, returns light; if the theme is
+ /// light, returns dark.
+ SystemUiOverlayStyle resolvedSystemUiOverlayStyle() {
+ Brightness brightness;
+ switch (themeMode) {
+ case ThemeMode.light:
+ brightness = Brightness.light;
+ case ThemeMode.dark:
+ brightness = Brightness.dark;
+ case ThemeMode.system:
+ brightness =
+ WidgetsBinding.instance.platformDispatcher.platformBrightness;
+ }
+
+ final SystemUiOverlayStyle overlayStyle = brightness == Brightness.dark
+ ? SystemUiOverlayStyle.light
+ : SystemUiOverlayStyle.dark;
+
+ return overlayStyle;
+ }
+
+ GalleryOptions copyWith({
+ ThemeMode? themeMode,
+ double? textScaleFactor,
+ CustomTextDirection? customTextDirection,
+ Locale? locale,
+ double? timeDilation,
+ TargetPlatform? platform,
+ bool? isTestMode,
+ }) {
+ return GalleryOptions(
+ themeMode: themeMode ?? this.themeMode,
+ textScaleFactor: textScaleFactor ?? _textScaleFactor,
+ customTextDirection: customTextDirection ?? this.customTextDirection,
+ locale: locale ?? this.locale,
+ timeDilation: timeDilation ?? this.timeDilation,
+ platform: platform ?? this.platform,
+ isTestMode: isTestMode ?? this.isTestMode,
+ );
+ }
+
+ @override
+ bool operator ==(Object other) =>
+ other is GalleryOptions &&
+ themeMode == other.themeMode &&
+ _textScaleFactor == other._textScaleFactor &&
+ customTextDirection == other.customTextDirection &&
+ locale == other.locale &&
+ timeDilation == other.timeDilation &&
+ platform == other.platform &&
+ isTestMode == other.isTestMode;
+
+ @override
+ int get hashCode => Object.hash(
+ themeMode,
+ _textScaleFactor,
+ customTextDirection,
+ locale,
+ timeDilation,
+ platform,
+ isTestMode,
+ );
+
+ static GalleryOptions of(BuildContext context) {
+ final _ModelBindingScope scope =
+ context.dependOnInheritedWidgetOfExactType<_ModelBindingScope>()!;
+ return scope.modelBindingState.currentModel;
+ }
+
+ static void update(BuildContext context, GalleryOptions newModel) {
+ final _ModelBindingScope scope =
+ context.dependOnInheritedWidgetOfExactType<_ModelBindingScope>()!;
+ scope.modelBindingState.updateModel(newModel);
+ }
+}
+
+// Applies text GalleryOptions to a widget
+class ApplyTextOptions extends StatelessWidget {
+ const ApplyTextOptions({
+ super.key,
+ required this.child,
+ });
+
+ final Widget child;
+
+ @override
+ Widget build(BuildContext context) {
+ final GalleryOptions options = GalleryOptions.of(context);
+ final TextDirection? textDirection = options.resolvedTextDirection();
+ final double textScaleFactor = options.textScaleFactor(context);
+
+ final Widget widget = MediaQuery(
+ data: MediaQuery.of(context).copyWith(
+ // ignore: deprecated_member_use
+ textScaleFactor: textScaleFactor,
+ ),
+ child: child,
+ );
+ return textDirection == null
+ ? widget
+ : Directionality(
+ textDirection: textDirection,
+ child: widget,
+ );
+ }
+}
+
+// Everything below is boilerplate except code relating to time dilation.
+// See https://medium.com/flutter/managing-flutter-application-state-with-inheritedwidgets-1140452befe1
+
+class _ModelBindingScope extends InheritedWidget {
+ const _ModelBindingScope({
+ required this.modelBindingState,
+ required super.child,
+ });
+
+ final _ModelBindingState modelBindingState;
+
+ @override
+ bool updateShouldNotify(_ModelBindingScope oldWidget) => true;
+}
+
+class ModelBinding extends StatefulWidget {
+ const ModelBinding({
+ super.key,
+ required this.initialModel,
+ required this.child,
+ });
+
+ final GalleryOptions initialModel;
+ final Widget child;
+
+ @override
+ State createState() => _ModelBindingState();
+}
+
+class _ModelBindingState extends State {
+ late GalleryOptions currentModel;
+ Timer? _timeDilationTimer;
+
+ @override
+ void initState() {
+ super.initState();
+ currentModel = widget.initialModel;
+ }
+
+ @override
+ void dispose() {
+ _timeDilationTimer?.cancel();
+ _timeDilationTimer = null;
+ super.dispose();
+ }
+
+ void handleTimeDilation(GalleryOptions newModel) {
+ if (currentModel.timeDilation != newModel.timeDilation) {
+ _timeDilationTimer?.cancel();
+ _timeDilationTimer = null;
+ if (newModel.timeDilation > 1) {
+ // We delay the time dilation change long enough that the user can see
+ // that UI has started reacting and then we slam on the brakes so that
+ // they see that the time is in fact now dilated.
+ _timeDilationTimer = Timer(const Duration(milliseconds: 150), () {
+ timeDilation = newModel.timeDilation;
+ });
+ } else {
+ timeDilation = newModel.timeDilation;
+ }
+ }
+ }
+
+ void updateModel(GalleryOptions newModel) {
+ if (newModel != currentModel) {
+ handleTimeDilation(newModel);
+ setState(() {
+ currentModel = newModel;
+ });
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return _ModelBindingScope(
+ modelBindingState: this,
+ child: widget.child,
+ );
+ }
+}
diff --git a/dev/integration_tests/new_gallery/lib/data/icons.dart b/dev/integration_tests/new_gallery/lib/data/icons.dart
new file mode 100644
index 0000000000..970501f496
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/data/icons.dart
@@ -0,0 +1,174 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+
+class GalleryIcons {
+ GalleryIcons._();
+
+ static const IconData tooltip = IconData(
+ 0xe900,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData textFieldsAlt = IconData(
+ 0xe901,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData tabs = IconData(
+ 0xe902,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData switches = IconData(
+ 0xe903,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData sliders = IconData(
+ 0xe904,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData shrine = IconData(
+ 0xe905,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData sentimentVerySatisfied = IconData(
+ 0xe906,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData refresh = IconData(
+ 0xe907,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData progressActivity = IconData(
+ 0xe908,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData phoneIphone = IconData(
+ 0xe909,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData pageControl = IconData(
+ 0xe90a,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData moreVert = IconData(
+ 0xe90b,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData menu = IconData(
+ 0xe90c,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData listAlt = IconData(
+ 0xe90d,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData gridOn = IconData(
+ 0xe90e,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData expandAll = IconData(
+ 0xe90f,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData event = IconData(
+ 0xe910,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData driveVideo = IconData(
+ 0xe911,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData dialogs = IconData(
+ 0xe912,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData dataTable = IconData(
+ 0xe913,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData customTypography = IconData(
+ 0xe914,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData colors = IconData(
+ 0xe915,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData chips = IconData(
+ 0xe916,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData checkBox = IconData(
+ 0xe917,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData cards = IconData(
+ 0xe918,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData buttons = IconData(
+ 0xe919,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData bottomSheets = IconData(
+ 0xe91a,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData bottomNavigation = IconData(
+ 0xe91b,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData animation = IconData(
+ 0xe91c,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData accountBox = IconData(
+ 0xe91d,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData snackbar = IconData(
+ 0xe91e,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData categoryMdc = IconData(
+ 0xe91f,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData cupertinoProgress = IconData(
+ 0xe920,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData cupertinoPullToRefresh = IconData(
+ 0xe921,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData cupertinoSwitch = IconData(
+ 0xe922,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData genericButtons = IconData(
+ 0xe923,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData backdrop = IconData(
+ 0xe924,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData bottomAppBar = IconData(
+ 0xe925,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData bottomSheetPersistent = IconData(
+ 0xe926,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData listsLeaveBehind = IconData(
+ 0xe927,
+ fontFamily: 'GalleryIcons',
+ );
+ static const IconData navigationRail = Icons.vertical_split;
+ static const IconData appbar = Icons.web_asset;
+ static const IconData divider = Icons.credit_card;
+ static const IconData search = Icons.search;
+}
diff --git a/dev/integration_tests/new_gallery/lib/deferred_widget.dart b/dev/integration_tests/new_gallery/lib/deferred_widget.dart
new file mode 100644
index 0000000000..bcbff0eaa0
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/deferred_widget.dart
@@ -0,0 +1,120 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+import 'package:flutter/material.dart';
+
+typedef LibraryLoader = Future Function();
+typedef DeferredWidgetBuilder = Widget Function();
+
+/// Wraps the child inside a deferred module loader.
+///
+/// The child is created and a single instance of the Widget is maintained in
+/// state as long as closure to create widget stays the same.
+///
+class DeferredWidget extends StatefulWidget {
+ DeferredWidget(
+ this.libraryLoader,
+ this.createWidget, {
+ super.key,
+ Widget? placeholder,
+ }) : placeholder = placeholder ?? Container();
+
+ final LibraryLoader libraryLoader;
+ final DeferredWidgetBuilder createWidget;
+ final Widget placeholder;
+ static final Map> _moduleLoaders = >{};
+ static final Set _loadedModules = {};
+
+ static Future preload(LibraryLoader loader) {
+ if (!_moduleLoaders.containsKey(loader)) {
+ _moduleLoaders[loader] = loader().then((dynamic _) {
+ _loadedModules.add(loader);
+ });
+ }
+ return _moduleLoaders[loader]!;
+ }
+
+ @override
+ State createState() => _DeferredWidgetState();
+}
+
+class _DeferredWidgetState extends State {
+ _DeferredWidgetState();
+
+ Widget? _loadedChild;
+ DeferredWidgetBuilder? _loadedCreator;
+
+ @override
+ void initState() {
+ /// If module was already loaded immediately create widget instead of
+ /// waiting for future or zone turn.
+ if (DeferredWidget._loadedModules.contains(widget.libraryLoader)) {
+ _onLibraryLoaded();
+ } else {
+ DeferredWidget.preload(widget.libraryLoader)
+ .then((dynamic _) => _onLibraryLoaded());
+ }
+ super.initState();
+ }
+
+ void _onLibraryLoaded() {
+ setState(() {
+ _loadedCreator = widget.createWidget;
+ _loadedChild = _loadedCreator!();
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ /// If closure to create widget changed, create new instance, otherwise
+ /// treat as const Widget.
+ if (_loadedCreator != widget.createWidget && _loadedCreator != null) {
+ _loadedCreator = widget.createWidget;
+ _loadedChild = _loadedCreator!();
+ }
+ return _loadedChild ?? widget.placeholder;
+ }
+}
+
+/// Displays a progress indicator and text description explaining that
+/// the widget is a deferred component and is currently being installed.
+class DeferredLoadingPlaceholder extends StatelessWidget {
+ const DeferredLoadingPlaceholder({
+ super.key,
+ this.name = 'This widget',
+ });
+
+ final String name;
+
+ @override
+ Widget build(BuildContext context) {
+ return Center(
+ child: Container(
+ decoration: BoxDecoration(
+ color: Colors.grey[700],
+ border: Border.all(
+ width: 20,
+ color: Colors.grey[700]!,
+ ),
+ borderRadius: const BorderRadius.all(Radius.circular(10))),
+ width: 250,
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text('$name is installing.',
+ style: Theme.of(context).textTheme.headlineMedium),
+ Container(height: 10),
+ Text(
+ '$name is a deferred component which are downloaded and installed at runtime.',
+ style: Theme.of(context).textTheme.bodyLarge),
+ Container(height: 20),
+ const Center(child: CircularProgressIndicator()),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_activity_indicator_demo.dart b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_activity_indicator_demo.dart
new file mode 100644
index 0000000000..db8bdd9912
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_activity_indicator_demo.dart
@@ -0,0 +1,29 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/cupertino.dart';
+import '../../gallery_localizations.dart';
+
+// BEGIN cupertinoActivityIndicatorDemo
+
+class CupertinoProgressIndicatorDemo extends StatelessWidget {
+ const CupertinoProgressIndicatorDemo({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return CupertinoPageScaffold(
+ navigationBar: CupertinoNavigationBar(
+ automaticallyImplyLeading: false,
+ middle: Text(
+ GalleryLocalizations.of(context)!.demoCupertinoActivityIndicatorTitle,
+ ),
+ ),
+ child: const Center(
+ child: CupertinoActivityIndicator(),
+ ),
+ );
+ }
+}
+
+// END
diff --git a/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_alert_demo.dart b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_alert_demo.dart
new file mode 100644
index 0000000000..96cc821c2d
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_alert_demo.dart
@@ -0,0 +1,432 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/cupertino.dart';
+
+import '../../data/gallery_options.dart';
+import '../../gallery_localizations.dart';
+import 'demo_types.dart';
+
+// BEGIN cupertinoAlertDemo
+
+class CupertinoAlertDemo extends StatefulWidget {
+ const CupertinoAlertDemo({
+ super.key,
+ required this.type,
+ });
+
+ final AlertDemoType type;
+
+ @override
+ State createState() => _CupertinoAlertDemoState();
+}
+
+class _CupertinoAlertDemoState extends State
+ with RestorationMixin {
+ RestorableStringN lastSelectedValue = RestorableStringN(null);
+ late RestorableRouteFuture _alertDialogRoute;
+ late RestorableRouteFuture _alertWithTitleDialogRoute;
+ late RestorableRouteFuture _alertWithButtonsDialogRoute;
+ late RestorableRouteFuture _alertWithButtonsOnlyDialogRoute;
+ late RestorableRouteFuture _modalPopupRoute;
+
+ @override
+ String get restorationId => 'cupertino_alert_demo';
+
+ @override
+ void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
+ registerForRestoration(
+ lastSelectedValue,
+ 'last_selected_value',
+ );
+ registerForRestoration(
+ _alertDialogRoute,
+ 'alert_demo_dialog_route',
+ );
+ registerForRestoration(
+ _alertWithTitleDialogRoute,
+ 'alert_with_title_press_demo_dialog_route',
+ );
+ registerForRestoration(
+ _alertWithButtonsDialogRoute,
+ 'alert_with_title_press_demo_dialog_route',
+ );
+ registerForRestoration(
+ _alertWithButtonsOnlyDialogRoute,
+ 'alert_with_title_press_demo_dialog_route',
+ );
+ registerForRestoration(
+ _modalPopupRoute,
+ 'modal_popup_route',
+ );
+ }
+
+ void _setSelectedValue(String value) {
+ setState(() {
+ lastSelectedValue.value = value;
+ });
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ _alertDialogRoute = RestorableRouteFuture(
+ onPresent: (NavigatorState navigator, Object? arguments) {
+ return navigator.restorablePush(_alertDemoDialog);
+ },
+ onComplete: _setSelectedValue,
+ );
+ _alertWithTitleDialogRoute = RestorableRouteFuture(
+ onPresent: (NavigatorState navigator, Object? arguments) {
+ return navigator.restorablePush(_alertWithTitleDialog);
+ },
+ onComplete: _setSelectedValue,
+ );
+ _alertWithButtonsDialogRoute = RestorableRouteFuture(
+ onPresent: (NavigatorState navigator, Object? arguments) {
+ return navigator.restorablePush(_alertWithButtonsDialog);
+ },
+ onComplete: _setSelectedValue,
+ );
+ _alertWithButtonsOnlyDialogRoute = RestorableRouteFuture(
+ onPresent: (NavigatorState navigator, Object? arguments) {
+ return navigator.restorablePush(_alertWithButtonsOnlyDialog);
+ },
+ onComplete: _setSelectedValue,
+ );
+ _modalPopupRoute = RestorableRouteFuture(
+ onPresent: (NavigatorState navigator, Object? arguments) {
+ return navigator.restorablePush(_modalRoute);
+ },
+ onComplete: _setSelectedValue,
+ );
+ }
+
+ String _title(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ switch (widget.type) {
+ case AlertDemoType.alert:
+ return localizations.demoCupertinoAlertTitle;
+ case AlertDemoType.alertTitle:
+ return localizations.demoCupertinoAlertWithTitleTitle;
+ case AlertDemoType.alertButtons:
+ return localizations.demoCupertinoAlertButtonsTitle;
+ case AlertDemoType.alertButtonsOnly:
+ return localizations.demoCupertinoAlertButtonsOnlyTitle;
+ case AlertDemoType.actionSheet:
+ return localizations.demoCupertinoActionSheetTitle;
+ }
+ }
+
+ static Route _alertDemoDialog(
+ BuildContext context,
+ Object? arguments,
+ ) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ return CupertinoDialogRoute(
+ context: context,
+ builder: (BuildContext context) => ApplyTextOptions(
+ child: CupertinoAlertDialog(
+ title: Text(localizations.dialogDiscardTitle),
+ actions: [
+ CupertinoDialogAction(
+ isDestructiveAction: true,
+ onPressed: () {
+ Navigator.of(
+ context,
+ ).pop(localizations.cupertinoAlertDiscard);
+ },
+ child: Text(
+ localizations.cupertinoAlertDiscard,
+ ),
+ ),
+ CupertinoDialogAction(
+ isDefaultAction: true,
+ onPressed: () => Navigator.of(
+ context,
+ ).pop(
+ localizations.cupertinoAlertCancel,
+ ),
+ child: Text(
+ localizations.cupertinoAlertCancel,
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ static Route _alertWithTitleDialog(
+ BuildContext context,
+ Object? arguments,
+ ) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ return CupertinoDialogRoute(
+ context: context,
+ builder: (BuildContext context) => ApplyTextOptions(
+ child: CupertinoAlertDialog(
+ title: Text(
+ localizations.cupertinoAlertLocationTitle,
+ ),
+ content: Text(
+ localizations.cupertinoAlertLocationDescription,
+ ),
+ actions: [
+ CupertinoDialogAction(
+ onPressed: () => Navigator.of(
+ context,
+ ).pop(
+ localizations.cupertinoAlertDontAllow,
+ ),
+ child: Text(
+ localizations.cupertinoAlertDontAllow,
+ ),
+ ),
+ CupertinoDialogAction(
+ onPressed: () => Navigator.of(
+ context,
+ ).pop(
+ localizations.cupertinoAlertAllow,
+ ),
+ child: Text(
+ localizations.cupertinoAlertAllow,
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ static Route _alertWithButtonsDialog(
+ BuildContext context,
+ Object? arguments,
+ ) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ return CupertinoDialogRoute(
+ context: context,
+ builder: (BuildContext context) => ApplyTextOptions(
+ child: CupertinoDessertDialog(
+ title: Text(
+ localizations.cupertinoAlertFavoriteDessert,
+ ),
+ content: Text(
+ localizations.cupertinoAlertDessertDescription,
+ ),
+ ),
+ ),
+ );
+ }
+
+ static Route _alertWithButtonsOnlyDialog(
+ BuildContext context,
+ Object? arguments,
+ ) {
+ return CupertinoDialogRoute(
+ context: context,
+ builder: (BuildContext context) => const ApplyTextOptions(
+ child: CupertinoDessertDialog(),
+ ),
+ );
+ }
+
+ static Route _modalRoute(
+ BuildContext context,
+ Object? arguments,
+ ) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ return CupertinoModalPopupRoute(
+ builder: (BuildContext context) => ApplyTextOptions(
+ child: CupertinoActionSheet(
+ title: Text(
+ localizations.cupertinoAlertFavoriteDessert,
+ ),
+ message: Text(
+ localizations.cupertinoAlertDessertDescription,
+ ),
+ actions: [
+ CupertinoActionSheetAction(
+ onPressed: () => Navigator.of(
+ context,
+ ).pop(
+ localizations.cupertinoAlertCheesecake,
+ ),
+ child: Text(
+ localizations.cupertinoAlertCheesecake,
+ ),
+ ),
+ CupertinoActionSheetAction(
+ onPressed: () => Navigator.of(
+ context,
+ ).pop(
+ localizations.cupertinoAlertTiramisu,
+ ),
+ child: Text(
+ localizations.cupertinoAlertTiramisu,
+ ),
+ ),
+ CupertinoActionSheetAction(
+ onPressed: () => Navigator.of(
+ context,
+ ).pop(
+ localizations.cupertinoAlertApplePie,
+ ),
+ child: Text(
+ localizations.cupertinoAlertApplePie,
+ ),
+ ),
+ ],
+ cancelButton: CupertinoActionSheetAction(
+ isDefaultAction: true,
+ onPressed: () => Navigator.of(
+ context,
+ ).pop(
+ localizations.cupertinoAlertCancel,
+ ),
+ child: Text(
+ localizations.cupertinoAlertCancel,
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return CupertinoPageScaffold(
+ navigationBar: CupertinoNavigationBar(
+ automaticallyImplyLeading: false,
+ middle: Text(_title(context)),
+ ),
+ child: Builder(
+ builder: (BuildContext context) {
+ return Column(
+ children: [
+ Expanded(
+ child: Center(
+ child: CupertinoButton.filled(
+ onPressed: () {
+ switch (widget.type) {
+ case AlertDemoType.alert:
+ _alertDialogRoute.present();
+ case AlertDemoType.alertTitle:
+ _alertWithTitleDialogRoute.present();
+ case AlertDemoType.alertButtons:
+ _alertWithButtonsDialogRoute.present();
+ case AlertDemoType.alertButtonsOnly:
+ _alertWithButtonsOnlyDialogRoute.present();
+ case AlertDemoType.actionSheet:
+ _modalPopupRoute.present();
+ }
+ },
+ child: Text(
+ GalleryLocalizations.of(context)!.cupertinoShowAlert,
+ ),
+ ),
+ ),
+ ),
+ if (lastSelectedValue.value != null)
+ Padding(
+ padding: const EdgeInsets.all(16),
+ child: Text(
+ GalleryLocalizations.of(context)!
+ .dialogSelectedOption(lastSelectedValue.value!),
+ style: CupertinoTheme.of(context).textTheme.textStyle,
+ textAlign: TextAlign.center,
+ ),
+ ),
+ ],
+ );
+ },
+ ),
+ );
+ }
+}
+
+class CupertinoDessertDialog extends StatelessWidget {
+ const CupertinoDessertDialog({
+ super.key,
+ this.title,
+ this.content,
+ });
+
+ final Widget? title;
+ final Widget? content;
+
+ @override
+ Widget build(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ return CupertinoAlertDialog(
+ title: title,
+ content: content,
+ actions: [
+ CupertinoDialogAction(
+ onPressed: () {
+ Navigator.of(
+ context,
+ ).pop(
+ localizations.cupertinoAlertCheesecake,
+ );
+ },
+ child: Text(
+ localizations.cupertinoAlertCheesecake,
+ ),
+ ),
+ CupertinoDialogAction(
+ onPressed: () {
+ Navigator.of(
+ context,
+ ).pop(
+ localizations.cupertinoAlertTiramisu,
+ );
+ },
+ child: Text(
+ localizations.cupertinoAlertTiramisu,
+ ),
+ ),
+ CupertinoDialogAction(
+ onPressed: () {
+ Navigator.of(
+ context,
+ ).pop(
+ localizations.cupertinoAlertApplePie,
+ );
+ },
+ child: Text(
+ localizations.cupertinoAlertApplePie,
+ ),
+ ),
+ CupertinoDialogAction(
+ onPressed: () {
+ Navigator.of(
+ context,
+ ).pop(
+ localizations.cupertinoAlertChocolateBrownie,
+ );
+ },
+ child: Text(
+ localizations.cupertinoAlertChocolateBrownie,
+ ),
+ ),
+ CupertinoDialogAction(
+ isDestructiveAction: true,
+ onPressed: () {
+ Navigator.of(
+ context,
+ ).pop(
+ localizations.cupertinoAlertCancel,
+ );
+ },
+ child: Text(
+ localizations.cupertinoAlertCancel,
+ ),
+ ),
+ ],
+ );
+ }
+}
+
+// END
diff --git a/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_button_demo.dart b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_button_demo.dart
new file mode 100644
index 0000000000..971f3bc6aa
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_button_demo.dart
@@ -0,0 +1,60 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/cupertino.dart';
+import '../../gallery_localizations.dart';
+
+// BEGIN cupertinoButtonDemo
+
+class CupertinoButtonDemo extends StatelessWidget {
+ const CupertinoButtonDemo({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ return CupertinoPageScaffold(
+ navigationBar: CupertinoNavigationBar(
+ automaticallyImplyLeading: false,
+ middle: Text(localizations.demoCupertinoButtonsTitle),
+ ),
+ child: Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ CupertinoButton(
+ onPressed: () {},
+ child: Text(
+ localizations.cupertinoButton,
+ ),
+ ),
+ const SizedBox(height: 16),
+ CupertinoButton.filled(
+ onPressed: () {},
+ child: Text(
+ localizations.cupertinoButtonWithBackground,
+ ),
+ ),
+ const SizedBox(height: 30),
+ // Disabled buttons
+ CupertinoButton(
+ onPressed: null,
+ child: Text(
+ localizations.cupertinoButton,
+ ),
+ ),
+ const SizedBox(height: 16),
+ CupertinoButton.filled(
+ onPressed: null,
+ child: Text(
+ localizations.cupertinoButtonWithBackground,
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
+
+// END
diff --git a/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_context_menu_demo.dart b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_context_menu_demo.dart
new file mode 100644
index 0000000000..34a70f6569
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_context_menu_demo.dart
@@ -0,0 +1,71 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import '../../gallery_localizations.dart';
+
+// BEGIN cupertinoContextMenuDemo
+
+class CupertinoContextMenuDemo extends StatelessWidget {
+ const CupertinoContextMenuDemo({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ final GalleryLocalizations galleryLocalizations = GalleryLocalizations.of(context)!;
+ return CupertinoPageScaffold(
+ navigationBar: CupertinoNavigationBar(
+ automaticallyImplyLeading: false,
+ middle: Text(
+ galleryLocalizations.demoCupertinoContextMenuTitle,
+ ),
+ ),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Center(
+ child: SizedBox(
+ width: 100,
+ height: 100,
+ child: CupertinoContextMenu(
+ actions: [
+ CupertinoContextMenuAction(
+ onPressed: () {
+ Navigator.pop(context);
+ },
+ child: Text(
+ galleryLocalizations.demoCupertinoContextMenuActionOne,
+ ),
+ ),
+ CupertinoContextMenuAction(
+ onPressed: () {
+ Navigator.pop(context);
+ },
+ child: Text(
+ galleryLocalizations.demoCupertinoContextMenuActionTwo,
+ ),
+ ),
+ ],
+ child: const FlutterLogo(size: 250),
+ ),
+ ),
+ ),
+ const SizedBox(height: 20),
+ Padding(
+ padding: const EdgeInsets.all(30),
+ child: Text(
+ galleryLocalizations.demoCupertinoContextMenuActionText,
+ textAlign: TextAlign.center,
+ style: const TextStyle(
+ color: Colors.black,
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+// END
diff --git a/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_demos.dart b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_demos.dart
new file mode 100644
index 0000000000..c365a421e1
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_demos.dart
@@ -0,0 +1,17 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+export 'package:gallery/demos/cupertino/cupertino_activity_indicator_demo.dart';
+export 'package:gallery/demos/cupertino/cupertino_alert_demo.dart';
+export 'package:gallery/demos/cupertino/cupertino_button_demo.dart';
+export 'package:gallery/demos/cupertino/cupertino_context_menu_demo.dart';
+export 'package:gallery/demos/cupertino/cupertino_navigation_bar_demo.dart';
+export 'package:gallery/demos/cupertino/cupertino_picker_demo.dart';
+export 'package:gallery/demos/cupertino/cupertino_scrollbar_demo.dart';
+export 'package:gallery/demos/cupertino/cupertino_search_text_field_demo.dart';
+export 'package:gallery/demos/cupertino/cupertino_segmented_control_demo.dart';
+export 'package:gallery/demos/cupertino/cupertino_slider_demo.dart';
+export 'package:gallery/demos/cupertino/cupertino_switch_demo.dart';
+export 'package:gallery/demos/cupertino/cupertino_tab_bar_demo.dart';
+export 'package:gallery/demos/cupertino/cupertino_text_field_demo.dart';
diff --git a/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_navigation_bar_demo.dart b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_navigation_bar_demo.dart
new file mode 100644
index 0000000000..073c1f5d87
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_navigation_bar_demo.dart
@@ -0,0 +1,112 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import '../../gallery_localizations.dart';
+
+// BEGIN cupertinoNavigationBarDemo
+
+class CupertinoNavigationBarDemo extends StatelessWidget {
+ const CupertinoNavigationBarDemo({super.key});
+
+ static const String homeRoute = '/home';
+ static const String secondPageRoute = '/home/item';
+
+ @override
+ Widget build(BuildContext context) {
+ return Navigator(
+ restorationScopeId: 'navigator',
+ initialRoute: CupertinoNavigationBarDemo.homeRoute,
+ onGenerateRoute: (RouteSettings settings) {
+ switch (settings.name) {
+ case CupertinoNavigationBarDemo.homeRoute:
+ return _NoAnimationCupertinoPageRoute(
+ title: GalleryLocalizations.of(context)!
+ .demoCupertinoNavigationBarTitle,
+ settings: settings,
+ builder: (BuildContext context) => _FirstPage(),
+ );
+ case CupertinoNavigationBarDemo.secondPageRoute:
+ final Map arguments = settings.arguments! as Map;
+ final String? title = arguments['pageTitle'] as String?;
+ return CupertinoPageRoute(
+ title: title,
+ settings: settings,
+ builder: (BuildContext context) => _SecondPage(),
+ );
+ }
+ return null;
+ },
+ );
+ }
+}
+
+class _FirstPage extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return CupertinoPageScaffold(
+ child: CustomScrollView(
+ slivers: [
+ const CupertinoSliverNavigationBar(
+ automaticallyImplyLeading: false,
+ ),
+ SliverPadding(
+ padding:
+ MediaQuery.of(context).removePadding(removeTop: true).padding,
+ sliver: SliverList(
+ delegate: SliverChildBuilderDelegate(
+ (BuildContext context, int index) {
+ final String title = GalleryLocalizations.of(context)!
+ .starterAppDrawerItem(index + 1);
+ return ListTile(
+ onTap: () {
+ Navigator.of(context).restorablePushNamed(
+ CupertinoNavigationBarDemo.secondPageRoute,
+ arguments: {'pageTitle': title},
+ );
+ },
+ title: Text(title),
+ );
+ },
+ childCount: 20,
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+class _SecondPage extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return CupertinoPageScaffold(
+ navigationBar: const CupertinoNavigationBar(),
+ child: Container(),
+ );
+ }
+}
+
+/// A CupertinoPageRoute without any transition animations.
+class _NoAnimationCupertinoPageRoute extends CupertinoPageRoute {
+ _NoAnimationCupertinoPageRoute({
+ required super.builder,
+ super.settings,
+ super.title,
+ });
+
+ @override
+ Widget buildTransitions(
+ BuildContext context,
+ Animation animation,
+ Animation secondaryAnimation,
+ Widget child,
+ ) {
+ return child;
+ }
+}
+
+// END
diff --git a/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_picker_demo.dart b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_picker_demo.dart
new file mode 100644
index 0000000000..d71c539b22
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_picker_demo.dart
@@ -0,0 +1,322 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/cupertino.dart';
+import 'package:intl/intl.dart';
+
+import '../../gallery_localizations.dart';
+
+// BEGIN cupertinoPickersDemo
+
+class CupertinoPickerDemo extends StatefulWidget {
+ const CupertinoPickerDemo({super.key});
+
+ @override
+ State createState() => _CupertinoPickerDemoState();
+}
+
+class _CupertinoPickerDemoState extends State {
+ Duration timer = Duration.zero;
+
+ // Value that is shown in the date picker in date mode.
+ DateTime date = DateTime.now();
+
+ // Value that is shown in the date picker in time mode.
+ DateTime time = DateTime.now();
+
+ // Value that is shown in the date picker in dateAndTime mode.
+ DateTime dateTime = DateTime.now();
+
+ int _selectedWeekday = 0;
+
+ static List getDaysOfWeek([String? locale]) {
+ final DateTime now = DateTime.now();
+ final DateTime firstDayOfWeek = now.subtract(Duration(days: now.weekday - 1));
+ return List.generate(7, (int index) => index)
+ .map((int value) => DateFormat(DateFormat.WEEKDAY, locale)
+ .format(firstDayOfWeek.add(Duration(days: value))))
+ .toList();
+ }
+
+ void _showDemoPicker({
+ required BuildContext context,
+ required Widget child,
+ }) {
+ final CupertinoThemeData themeData = CupertinoTheme.of(context);
+ final CupertinoTheme dialogBody = CupertinoTheme(
+ data: themeData,
+ child: child,
+ );
+
+ showCupertinoModalPopup(
+ context: context,
+ builder: (BuildContext context) => dialogBody,
+ );
+ }
+
+ Widget _buildDatePicker(BuildContext context) {
+ return MouseRegion(
+ cursor: SystemMouseCursors.click,
+ child: GestureDetector(
+ onTap: () {
+ _showDemoPicker(
+ context: context,
+ child: _BottomPicker(
+ child: CupertinoDatePicker(
+ backgroundColor:
+ CupertinoColors.systemBackground.resolveFrom(context),
+ mode: CupertinoDatePickerMode.date,
+ initialDateTime: date,
+ onDateTimeChanged: (DateTime newDateTime) {
+ setState(() => date = newDateTime);
+ },
+ ),
+ ),
+ );
+ },
+ child: _Menu(
+ children: [
+ Text(GalleryLocalizations.of(context)!.demoCupertinoPickerDate),
+ Text(
+ DateFormat.yMMMMd().format(date),
+ style: const TextStyle(color: CupertinoColors.inactiveGray),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ Widget _buildTimePicker(BuildContext context) {
+ return MouseRegion(
+ cursor: SystemMouseCursors.click,
+ child: GestureDetector(
+ onTap: () {
+ _showDemoPicker(
+ context: context,
+ child: _BottomPicker(
+ child: CupertinoDatePicker(
+ backgroundColor:
+ CupertinoColors.systemBackground.resolveFrom(context),
+ mode: CupertinoDatePickerMode.time,
+ initialDateTime: time,
+ onDateTimeChanged: (DateTime newDateTime) {
+ setState(() => time = newDateTime);
+ },
+ ),
+ ),
+ );
+ },
+ child: _Menu(
+ children: [
+ Text(GalleryLocalizations.of(context)!.demoCupertinoPickerTime),
+ Text(
+ DateFormat.jm().format(time),
+ style: const TextStyle(color: CupertinoColors.inactiveGray),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ Widget _buildDateAndTimePicker(BuildContext context) {
+ return MouseRegion(
+ cursor: SystemMouseCursors.click,
+ child: GestureDetector(
+ onTap: () {
+ _showDemoPicker(
+ context: context,
+ child: _BottomPicker(
+ child: CupertinoDatePicker(
+ backgroundColor:
+ CupertinoColors.systemBackground.resolveFrom(context),
+ initialDateTime: dateTime,
+ onDateTimeChanged: (DateTime newDateTime) {
+ setState(() => dateTime = newDateTime);
+ },
+ ),
+ ),
+ );
+ },
+ child: _Menu(
+ children: [
+ Text(GalleryLocalizations.of(context)!.demoCupertinoPickerDateTime),
+ Flexible(
+ child: Text(
+ DateFormat.yMMMd().add_jm().format(dateTime),
+ style: const TextStyle(color: CupertinoColors.inactiveGray),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ Widget _buildCountdownTimerPicker(BuildContext context) {
+ return MouseRegion(
+ cursor: SystemMouseCursors.click,
+ child: GestureDetector(
+ onTap: () {
+ _showDemoPicker(
+ context: context,
+ child: _BottomPicker(
+ child: CupertinoTimerPicker(
+ backgroundColor:
+ CupertinoColors.systemBackground.resolveFrom(context),
+ initialTimerDuration: timer,
+ onTimerDurationChanged: (Duration newTimer) {
+ setState(() => timer = newTimer);
+ },
+ ),
+ ),
+ );
+ },
+ child: _Menu(
+ children: [
+ Text(GalleryLocalizations.of(context)!.demoCupertinoPickerTimer),
+ Text(
+ '${timer.inHours}:'
+ '${(timer.inMinutes % 60).toString().padLeft(2, '0')}:'
+ '${(timer.inSeconds % 60).toString().padLeft(2, '0')}',
+ style: const TextStyle(color: CupertinoColors.inactiveGray),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ Widget _buildPicker(BuildContext context) {
+ final String? locale = GalleryLocalizations.of(context)?.localeName;
+ final List daysOfWeek = getDaysOfWeek(locale);
+ return MouseRegion(
+ cursor: SystemMouseCursors.click,
+ child: GestureDetector(
+ onTap: () {
+ _showDemoPicker(
+ context: context,
+ child: _BottomPicker(
+ child: CupertinoPicker(
+ backgroundColor:
+ CupertinoColors.systemBackground.resolveFrom(context),
+ itemExtent: 32.0,
+ magnification: 1.22,
+ squeeze: 1.2,
+ useMagnifier: true,
+ // This is called when selected item is changed.
+ onSelectedItemChanged: (int selectedItem) {
+ setState(() {
+ _selectedWeekday = selectedItem;
+ });
+ },
+ children: List.generate(daysOfWeek.length, (int index) {
+ return Center(
+ child: Text(
+ daysOfWeek[index],
+ ),
+ );
+ }),
+ ),
+ ),
+ );
+ },
+ child: _Menu(
+ children: [
+ Text(GalleryLocalizations.of(context)!.demoCupertinoPicker),
+ Text(
+ daysOfWeek[_selectedWeekday],
+ style: const TextStyle(color: CupertinoColors.inactiveGray),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return CupertinoPageScaffold(
+ navigationBar: CupertinoNavigationBar(
+ automaticallyImplyLeading: false,
+ middle:
+ Text(GalleryLocalizations.of(context)!.demoCupertinoPickerTitle),
+ ),
+ child: DefaultTextStyle(
+ style: CupertinoTheme.of(context).textTheme.textStyle,
+ child: ListView(
+ children: [
+ const SizedBox(height: 32),
+ _buildDatePicker(context),
+ _buildTimePicker(context),
+ _buildDateAndTimePicker(context),
+ _buildCountdownTimerPicker(context),
+ _buildPicker(context),
+ ],
+ ),
+ ),
+ );
+ }
+}
+
+class _BottomPicker extends StatelessWidget {
+ const _BottomPicker({required this.child});
+
+ final Widget child;
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ height: 216,
+ padding: const EdgeInsets.only(top: 6),
+ margin: EdgeInsets.only(
+ bottom: MediaQuery.of(context).viewInsets.bottom,
+ ),
+ color: CupertinoColors.systemBackground.resolveFrom(context),
+ child: DefaultTextStyle(
+ style: TextStyle(
+ color: CupertinoColors.label.resolveFrom(context),
+ fontSize: 22,
+ ),
+ child: GestureDetector(
+ // Blocks taps from propagating to the modal sheet and popping.
+ onTap: () {},
+ child: SafeArea(
+ top: false,
+ child: child,
+ ),
+ ),
+ ),
+ );
+ }
+}
+
+class _Menu extends StatelessWidget {
+ const _Menu({required this.children});
+
+ final List children;
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ decoration: const BoxDecoration(
+ border: Border(
+ top: BorderSide(color: CupertinoColors.inactiveGray, width: 0),
+ bottom: BorderSide(color: CupertinoColors.inactiveGray, width: 0),
+ ),
+ ),
+ height: 44,
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: children,
+ ),
+ ),
+ );
+ }
+}
+
+// END
diff --git a/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_scrollbar_demo.dart b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_scrollbar_demo.dart
new file mode 100644
index 0000000000..53f0e43181
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_scrollbar_demo.dart
@@ -0,0 +1,40 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/cupertino.dart';
+import '../../gallery_localizations.dart';
+
+// BEGIN cupertinoScrollbarDemo
+
+class CupertinoScrollbarDemo extends StatelessWidget {
+ const CupertinoScrollbarDemo({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ return CupertinoPageScaffold(
+ navigationBar: CupertinoNavigationBar(
+ automaticallyImplyLeading: false,
+ middle: Text(localizations.demoCupertinoScrollbarTitle),
+ ),
+ child: CupertinoScrollbar(
+ thickness: 6.0,
+ thicknessWhileDragging: 10.0,
+ radius: const Radius.circular(34.0),
+ radiusWhileDragging: Radius.zero,
+ child: ListView.builder(
+ itemCount: 120,
+ itemBuilder: (BuildContext context, int index) {
+ return Center(
+ child: Text('item $index',
+ style: CupertinoTheme.of(context).textTheme.textStyle),
+ );
+ },
+ ),
+ ),
+ );
+ }
+}
+
+// END
diff --git a/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_search_text_field_demo.dart b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_search_text_field_demo.dart
new file mode 100644
index 0000000000..611b749c02
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_search_text_field_demo.dart
@@ -0,0 +1,107 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import '../../gallery_localizations.dart';
+
+// BEGIN cupertinoSearchTextFieldDemo
+
+class CupertinoSearchTextFieldDemo extends StatefulWidget {
+ const CupertinoSearchTextFieldDemo({super.key});
+
+ @override
+ State createState() =>
+ _CupertinoSearchTextFieldDemoState();
+}
+
+class _CupertinoSearchTextFieldDemoState
+ extends State {
+ final List platforms = [
+ 'Android',
+ 'iOS',
+ 'Windows',
+ 'Linux',
+ 'MacOS',
+ 'Web'
+ ];
+
+ final TextEditingController _queryTextController = TextEditingController();
+ String _searchPlatform = '';
+ List filteredPlatforms = [];
+
+ @override
+ void initState() {
+ super.initState();
+ filteredPlatforms = platforms;
+ _queryTextController.addListener(() {
+ if (_queryTextController.text.isEmpty) {
+ setState(() {
+ _searchPlatform = '';
+ filteredPlatforms = platforms;
+ });
+ } else {
+ setState(() {
+ _searchPlatform = _queryTextController.text;
+ });
+ }
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ return CupertinoPageScaffold(
+ navigationBar: CupertinoNavigationBar(
+ automaticallyImplyLeading: false,
+ middle: Text(localizations.demoCupertinoSearchTextFieldTitle),
+ ),
+ child: SafeArea(
+ child: Column(
+ children: [
+ CupertinoSearchTextField(
+ controller: _queryTextController,
+ restorationId: 'search_text_field',
+ padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 12),
+ decoration: const BoxDecoration(
+ border: Border(
+ bottom: BorderSide(
+ width: 0,
+ color: CupertinoColors.inactiveGray,
+ ),
+ ),
+ ),
+ placeholder:
+ localizations.demoCupertinoSearchTextFieldPlaceholder,
+ ),
+ _buildPlatformList(),
+ ],
+ ),
+ ),
+ );
+ }
+
+ Widget _buildPlatformList() {
+ if (_searchPlatform.isNotEmpty) {
+ final List tempList = [];
+ for (int i = 0; i < filteredPlatforms.length; i++) {
+ if (filteredPlatforms[i]
+ .toLowerCase()
+ .contains(_searchPlatform.toLowerCase())) {
+ tempList.add(filteredPlatforms[i]);
+ }
+ }
+ filteredPlatforms = tempList;
+ }
+ return ListView.builder(
+ itemCount: filteredPlatforms.length,
+ shrinkWrap: true,
+ itemBuilder: (BuildContext context, int index) {
+ return ListTile(title: Text(filteredPlatforms[index]));
+ },
+ );
+ }
+}
+
+// END
diff --git a/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_segmented_control_demo.dart b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_segmented_control_demo.dart
new file mode 100644
index 0000000000..51ad30db4a
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_segmented_control_demo.dart
@@ -0,0 +1,96 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/cupertino.dart';
+
+import '../../gallery_localizations.dart';
+
+// BEGIN cupertinoSegmentedControlDemo
+
+class CupertinoSegmentedControlDemo extends StatefulWidget {
+ const CupertinoSegmentedControlDemo({super.key});
+
+ @override
+ State createState() =>
+ _CupertinoSegmentedControlDemoState();
+}
+
+class _CupertinoSegmentedControlDemoState
+ extends State with RestorationMixin {
+ RestorableInt currentSegment = RestorableInt(0);
+
+ @override
+ String get restorationId => 'cupertino_segmented_control';
+
+ @override
+ void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
+ registerForRestoration(currentSegment, 'current_segment');
+ }
+
+ void onValueChanged(int? newValue) {
+ setState(() {
+ currentSegment.value = newValue!;
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ const double segmentedControlMaxWidth = 500.0;
+ final Map children = {
+ 0: Text(localizations.colorsIndigo),
+ 1: Text(localizations.colorsTeal),
+ 2: Text(localizations.colorsCyan),
+ };
+
+ return CupertinoPageScaffold(
+ navigationBar: CupertinoNavigationBar(
+ automaticallyImplyLeading: false,
+ middle: Text(
+ localizations.demoCupertinoSegmentedControlTitle,
+ ),
+ ),
+ child: DefaultTextStyle(
+ style: CupertinoTheme.of(context)
+ .textTheme
+ .textStyle
+ .copyWith(fontSize: 13),
+ child: SafeArea(
+ child: ListView(
+ children: [
+ const SizedBox(height: 16),
+ SizedBox(
+ width: segmentedControlMaxWidth,
+ child: CupertinoSegmentedControl(
+ children: children,
+ onValueChanged: onValueChanged,
+ groupValue: currentSegment.value,
+ ),
+ ),
+ SizedBox(
+ width: segmentedControlMaxWidth,
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: CupertinoSlidingSegmentedControl(
+ children: children,
+ onValueChanged: onValueChanged,
+ groupValue: currentSegment.value,
+ ),
+ ),
+ ),
+ Container(
+ padding: const EdgeInsets.all(16),
+ height: 300,
+ alignment: Alignment.center,
+ child: children[currentSegment.value],
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
+
+// END
diff --git a/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_slider_demo.dart b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_slider_demo.dart
new file mode 100644
index 0000000000..dff7312ade
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_slider_demo.dart
@@ -0,0 +1,110 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/cupertino.dart';
+import '../../gallery_localizations.dart';
+
+// BEGIN cupertinoSliderDemo
+
+class CupertinoSliderDemo extends StatefulWidget {
+ const CupertinoSliderDemo({super.key});
+
+ @override
+ State createState() => _CupertinoSliderDemoState();
+}
+
+class _CupertinoSliderDemoState extends State
+ with RestorationMixin {
+ final RestorableDouble _value = RestorableDouble(25.0);
+ final RestorableDouble _discreteValue = RestorableDouble(20.0);
+
+ @override
+ String get restorationId => 'cupertino_slider_demo';
+
+ @override
+ void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
+ registerForRestoration(_value, 'value');
+ registerForRestoration(_discreteValue, 'discrete_value');
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ return CupertinoPageScaffold(
+ navigationBar: CupertinoNavigationBar(
+ automaticallyImplyLeading: false,
+ middle: Text(localizations.demoCupertinoSliderTitle),
+ ),
+ child: DefaultTextStyle(
+ style: CupertinoTheme.of(context).textTheme.textStyle,
+ child: Center(
+ child: Wrap(
+ children: [
+ Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const SizedBox(height: 32),
+ CupertinoSlider(
+ value: _value.value,
+ max: 100.0,
+ onChanged: (double value) {
+ setState(() {
+ _value.value = value;
+ });
+ },
+ ),
+ CupertinoSlider(
+ value: _value.value,
+ max: 100.0,
+ onChanged: null,
+ ),
+ MergeSemantics(
+ child: Text(
+ localizations.demoCupertinoSliderContinuous(
+ _value.value.toStringAsFixed(1),
+ ),
+ ),
+ ),
+ ],
+ ),
+ // Disabled sliders
+ // TODO(guidezpl): See https://github.com/flutter/flutter/issues/106691
+ Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const SizedBox(height: 32),
+ CupertinoSlider(
+ value: _discreteValue.value,
+ max: 100.0,
+ divisions: 5,
+ onChanged: (double value) {
+ setState(() {
+ _discreteValue.value = value;
+ });
+ },
+ ),
+ CupertinoSlider(
+ value: _discreteValue.value,
+ max: 100.0,
+ divisions: 5,
+ onChanged: null,
+ ),
+ MergeSemantics(
+ child: Text(
+ localizations.demoCupertinoSliderDiscrete(
+ _discreteValue.value.toStringAsFixed(1),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
+
+// END
diff --git a/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_switch_demo.dart b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_switch_demo.dart
new file mode 100644
index 0000000000..1ed3b25003
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_switch_demo.dart
@@ -0,0 +1,91 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/cupertino.dart';
+import '../../gallery_localizations.dart';
+
+// BEGIN cupertinoSwitchDemo
+
+class CupertinoSwitchDemo extends StatefulWidget {
+ const CupertinoSwitchDemo({super.key});
+
+ @override
+ State createState() => _CupertinoSwitchDemoState();
+}
+
+class _CupertinoSwitchDemoState extends State
+ with RestorationMixin {
+ final RestorableBool _switchValueA = RestorableBool(false);
+ final RestorableBool _switchValueB = RestorableBool(true);
+
+ @override
+ String get restorationId => 'cupertino_switch_demo';
+
+ @override
+ void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
+ registerForRestoration(_switchValueA, 'switch_valueA');
+ registerForRestoration(_switchValueB, 'switch_valueB');
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ return CupertinoPageScaffold(
+ navigationBar: CupertinoNavigationBar(
+ automaticallyImplyLeading: false,
+ middle: Text(
+ localizations.demoSelectionControlsSwitchTitle,
+ ),
+ ),
+ child: Center(
+ child: Semantics(
+ container: true,
+ label: localizations.demoSelectionControlsSwitchTitle,
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ CupertinoSwitch(
+ value: _switchValueA.value,
+ onChanged: (bool value) {
+ setState(() {
+ _switchValueA.value = value;
+ });
+ },
+ ),
+ CupertinoSwitch(
+ value: _switchValueB.value,
+ onChanged: (bool value) {
+ setState(() {
+ _switchValueB.value = value;
+ });
+ },
+ ),
+ ],
+ ),
+ // Disabled switches
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ CupertinoSwitch(
+ value: _switchValueA.value,
+ onChanged: null,
+ ),
+ CupertinoSwitch(
+ value: _switchValueB.value,
+ onChanged: null,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
+
+// END
diff --git a/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_tab_bar_demo.dart b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_tab_bar_demo.dart
new file mode 100644
index 0000000000..3aa4b70982
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_tab_bar_demo.dart
@@ -0,0 +1,92 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/cupertino.dart';
+
+import '../../gallery_localizations.dart';
+
+// BEGIN cupertinoNavigationDemo
+
+class _TabInfo {
+ const _TabInfo(this.title, this.icon);
+
+ final String title;
+ final IconData icon;
+}
+
+class CupertinoTabBarDemo extends StatelessWidget {
+ const CupertinoTabBarDemo({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ final List<_TabInfo> tabInfo = <_TabInfo>[
+ _TabInfo(
+ localizations.cupertinoTabBarHomeTab,
+ CupertinoIcons.home,
+ ),
+ _TabInfo(
+ localizations.cupertinoTabBarChatTab,
+ CupertinoIcons.conversation_bubble,
+ ),
+ _TabInfo(
+ localizations.cupertinoTabBarProfileTab,
+ CupertinoIcons.profile_circled,
+ ),
+ ];
+
+ return DefaultTextStyle(
+ style: CupertinoTheme.of(context).textTheme.textStyle,
+ child: CupertinoTabScaffold(
+ restorationId: 'cupertino_tab_scaffold',
+ tabBar: CupertinoTabBar(
+ items: [
+ for (final _TabInfo tabInfo in tabInfo)
+ BottomNavigationBarItem(
+ label: tabInfo.title,
+ icon: Icon(tabInfo.icon),
+ ),
+ ],
+ ),
+ tabBuilder: (BuildContext context, int index) {
+ return CupertinoTabView(
+ restorationScopeId: 'cupertino_tab_view_$index',
+ builder: (BuildContext context) => _CupertinoDemoTab(
+ title: tabInfo[index].title,
+ icon: tabInfo[index].icon,
+ ),
+ defaultTitle: tabInfo[index].title,
+ );
+ },
+ ),
+ );
+ }
+}
+
+class _CupertinoDemoTab extends StatelessWidget {
+ const _CupertinoDemoTab({
+ required this.title,
+ required this.icon,
+ });
+
+ final String title;
+ final IconData icon;
+
+ @override
+ Widget build(BuildContext context) {
+ return CupertinoPageScaffold(
+ navigationBar: const CupertinoNavigationBar(),
+ backgroundColor: CupertinoColors.systemBackground,
+ child: Center(
+ child: Icon(
+ icon,
+ semanticLabel: title,
+ size: 100,
+ ),
+ ),
+ );
+ }
+}
+
+// END
diff --git a/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_text_field_demo.dart b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_text_field_demo.dart
new file mode 100644
index 0000000000..7cf19ce337
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/cupertino/cupertino_text_field_demo.dart
@@ -0,0 +1,88 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/cupertino.dart';
+import '../../gallery_localizations.dart';
+
+// BEGIN cupertinoTextFieldDemo
+
+class CupertinoTextFieldDemo extends StatelessWidget {
+ const CupertinoTextFieldDemo({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ return CupertinoPageScaffold(
+ navigationBar: CupertinoNavigationBar(
+ automaticallyImplyLeading: false,
+ middle: Text(localizations.demoCupertinoTextFieldTitle),
+ ),
+ child: SafeArea(
+ child: ListView(
+ restorationId: 'text_field_demo_list_view',
+ padding: const EdgeInsets.all(16),
+ children: [
+ Padding(
+ padding: const EdgeInsets.symmetric(vertical: 8),
+ child: CupertinoTextField(
+ textInputAction: TextInputAction.next,
+ restorationId: 'email_address_text_field',
+ placeholder: localizations.demoTextFieldEmail,
+ keyboardType: TextInputType.emailAddress,
+ clearButtonMode: OverlayVisibilityMode.editing,
+ autocorrect: false,
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(vertical: 8),
+ child: CupertinoTextField(
+ textInputAction: TextInputAction.next,
+ restorationId: 'login_password_text_field',
+ placeholder: localizations.rallyLoginPassword,
+ clearButtonMode: OverlayVisibilityMode.editing,
+ obscureText: true,
+ autocorrect: false,
+ ),
+ ),
+ // Disabled text field
+ Padding(
+ padding: const EdgeInsets.symmetric(vertical: 8),
+ child: CupertinoTextField(
+ enabled: false,
+ textInputAction: TextInputAction.next,
+ restorationId: 'login_password_text_field_disabled',
+ placeholder: localizations.rallyLoginPassword,
+ clearButtonMode: OverlayVisibilityMode.editing,
+ obscureText: true,
+ autocorrect: false,
+ ),
+ ),
+ CupertinoTextField(
+ textInputAction: TextInputAction.done,
+ restorationId: 'pin_number_text_field',
+ prefix: const Icon(
+ CupertinoIcons.padlock_solid,
+ size: 28,
+ ),
+ padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 12),
+ clearButtonMode: OverlayVisibilityMode.editing,
+ keyboardType: TextInputType.number,
+ decoration: const BoxDecoration(
+ border: Border(
+ bottom: BorderSide(
+ width: 0,
+ color: CupertinoColors.inactiveGray,
+ ),
+ ),
+ ),
+ placeholder: localizations.demoCupertinoTextFieldPIN,
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
+
+// END
diff --git a/dev/integration_tests/new_gallery/lib/demos/cupertino/demo_types.dart b/dev/integration_tests/new_gallery/lib/demos/cupertino/demo_types.dart
new file mode 100644
index 0000000000..04e345e2c9
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/cupertino/demo_types.dart
@@ -0,0 +1,11 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+enum AlertDemoType {
+ alert,
+ alertTitle,
+ alertButtons,
+ alertButtonsOnly,
+ actionSheet,
+}
diff --git a/dev/integration_tests/new_gallery/lib/demos/material/app_bar_demo.dart b/dev/integration_tests/new_gallery/lib/demos/material/app_bar_demo.dart
new file mode 100644
index 0000000000..8d83f7f83c
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/material/app_bar_demo.dart
@@ -0,0 +1,73 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import '../../gallery_localizations.dart';
+
+// BEGIN appbarDemo
+
+class AppBarDemo extends StatelessWidget {
+ const AppBarDemo({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ final GalleryLocalizations localization = GalleryLocalizations.of(context)!;
+ return Scaffold(
+ appBar: AppBar(
+ leading: IconButton(
+ tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
+ icon: const Icon(Icons.menu),
+ onPressed: () {},
+ ),
+ title: Text(
+ localization.demoAppBarTitle,
+ ),
+ actions: [
+ IconButton(
+ tooltip: localization.starterAppTooltipFavorite,
+ icon: const Icon(
+ Icons.favorite,
+ ),
+ onPressed: () {},
+ ),
+ IconButton(
+ tooltip: localization.starterAppTooltipSearch,
+ icon: const Icon(
+ Icons.search,
+ ),
+ onPressed: () {},
+ ),
+ PopupMenuButton(
+ itemBuilder: (BuildContext context) {
+ return >[
+ PopupMenuItem(
+ child: Text(
+ localization.demoNavigationRailFirst,
+ ),
+ ),
+ PopupMenuItem(
+ child: Text(
+ localization.demoNavigationRailSecond,
+ ),
+ ),
+ PopupMenuItem(
+ child: Text(
+ localization.demoNavigationRailThird,
+ ),
+ ),
+ ];
+ },
+ )
+ ],
+ ),
+ body: Center(
+ child: Text(
+ localization.cupertinoTabBarHomeTab,
+ ),
+ ),
+ );
+ }
+}
+
+// END
diff --git a/dev/integration_tests/new_gallery/lib/demos/material/banner_demo.dart b/dev/integration_tests/new_gallery/lib/demos/material/banner_demo.dart
new file mode 100644
index 0000000000..fb614ba187
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/material/banner_demo.dart
@@ -0,0 +1,143 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import '../../gallery_localizations.dart';
+
+// BEGIN bannerDemo
+
+enum BannerDemoAction {
+ reset,
+ showMultipleActions,
+ showLeading,
+}
+
+class BannerDemo extends StatefulWidget {
+ const BannerDemo({super.key});
+
+ @override
+ State createState() => _BannerDemoState();
+}
+
+class _BannerDemoState extends State with RestorationMixin {
+ static const int _itemCount = 20;
+
+ @override
+ String get restorationId => 'banner_demo';
+
+ @override
+ void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
+ registerForRestoration(_displayBanner, 'display_banner');
+ registerForRestoration(_showMultipleActions, 'show_multiple_actions');
+ registerForRestoration(_showLeading, 'show_leading');
+ }
+
+ final RestorableBool _displayBanner = RestorableBool(true);
+ final RestorableBool _showMultipleActions = RestorableBool(true);
+ final RestorableBool _showLeading = RestorableBool(true);
+
+ @override
+ void dispose() {
+ _displayBanner.dispose();
+ _showMultipleActions.dispose();
+ _showLeading.dispose();
+ super.dispose();
+ }
+
+ void handleDemoAction(BannerDemoAction action) {
+ setState(() {
+ switch (action) {
+ case BannerDemoAction.reset:
+ _displayBanner.value = true;
+ _showMultipleActions.value = true;
+ _showLeading.value = true;
+ case BannerDemoAction.showMultipleActions:
+ _showMultipleActions.value = !_showMultipleActions.value;
+ case BannerDemoAction.showLeading:
+ _showLeading.value = !_showLeading.value;
+ }
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final ColorScheme colorScheme = Theme.of(context).colorScheme;
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ final MaterialBanner banner = MaterialBanner(
+ content: Text(localizations.bannerDemoText),
+ leading: _showLeading.value
+ ? CircleAvatar(
+ backgroundColor: colorScheme.primary,
+ child: Icon(Icons.access_alarm, color: colorScheme.onPrimary),
+ )
+ : null,
+ actions: [
+ TextButton(
+ onPressed: () {
+ setState(() {
+ _displayBanner.value = false;
+ });
+ },
+ child: Text(localizations.signIn),
+ ),
+ if (_showMultipleActions.value)
+ TextButton(
+ onPressed: () {
+ setState(() {
+ _displayBanner.value = false;
+ });
+ },
+ child: Text(localizations.dismiss),
+ ),
+ ],
+ backgroundColor: colorScheme.background,
+ );
+
+ return Scaffold(
+ appBar: AppBar(
+ automaticallyImplyLeading: false,
+ title: Text(localizations.demoBannerTitle),
+ actions: [
+ PopupMenuButton(
+ onSelected: handleDemoAction,
+ itemBuilder: (BuildContext context) => >[
+ PopupMenuItem(
+ value: BannerDemoAction.reset,
+ child: Text(localizations.bannerDemoResetText),
+ ),
+ const PopupMenuDivider(),
+ CheckedPopupMenuItem(
+ value: BannerDemoAction.showMultipleActions,
+ checked: _showMultipleActions.value,
+ child: Text(localizations.bannerDemoMultipleText),
+ ),
+ CheckedPopupMenuItem(
+ value: BannerDemoAction.showLeading,
+ checked: _showLeading.value,
+ child: Text(localizations.bannerDemoLeadingText),
+ ),
+ ],
+ ),
+ ],
+ ),
+ body: ListView.builder(
+ restorationId: 'banner_demo_list_view',
+ itemCount: _displayBanner.value ? _itemCount + 1 : _itemCount,
+ itemBuilder: (BuildContext context, int index) {
+ if (index == 0 && _displayBanner.value) {
+ return banner;
+ }
+ return ListTile(
+ title: Text(
+ localizations.starterAppDrawerItem(
+ _displayBanner.value ? index : index + 1),
+ ),
+ );
+ },
+ ),
+ );
+ }
+}
+
+// END
diff --git a/dev/integration_tests/new_gallery/lib/demos/material/bottom_app_bar_demo.dart b/dev/integration_tests/new_gallery/lib/demos/material/bottom_app_bar_demo.dart
new file mode 100644
index 0000000000..e6ec26e7a5
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/material/bottom_app_bar_demo.dart
@@ -0,0 +1,201 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
+import '../../gallery_localizations.dart';
+
+// BEGIN bottomAppBarDemo
+
+class BottomAppBarDemo extends StatefulWidget {
+ const BottomAppBarDemo({super.key});
+
+ @override
+ State createState() => _BottomAppBarDemoState();
+}
+
+class _BottomAppBarDemoState extends State
+ with RestorationMixin {
+ final RestorableBool _showFab = RestorableBool(true);
+ final RestorableBool _showNotch = RestorableBool(true);
+ final RestorableInt _currentFabLocation = RestorableInt(0);
+
+ @override
+ String get restorationId => 'bottom_app_bar_demo';
+
+ @override
+ void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
+ registerForRestoration(_showFab, 'show_fab');
+ registerForRestoration(_showNotch, 'show_notch');
+ registerForRestoration(_currentFabLocation, 'fab_location');
+ }
+
+ @override
+ void dispose() {
+ _showFab.dispose();
+ _showNotch.dispose();
+ _currentFabLocation.dispose();
+ super.dispose();
+ }
+
+ // Since FloatingActionButtonLocation is not an enum, the index of the
+ // selected FloatingActionButtonLocation is used for state restoration.
+ static const List _fabLocations = [
+ FloatingActionButtonLocation.endDocked,
+ FloatingActionButtonLocation.centerDocked,
+ FloatingActionButtonLocation.endFloat,
+ FloatingActionButtonLocation.centerFloat,
+ ];
+
+ void _onShowNotchChanged(bool value) {
+ setState(() {
+ _showNotch.value = value;
+ });
+ }
+
+ void _onShowFabChanged(bool value) {
+ setState(() {
+ _showFab.value = value;
+ });
+ }
+
+ void _onFabLocationChanged(int? value) {
+ setState(() {
+ _currentFabLocation.value = value!;
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ return Scaffold(
+ appBar: AppBar(
+ automaticallyImplyLeading: false,
+ title: Text(localizations.demoBottomAppBarTitle),
+ ),
+ body: ListView(
+ padding: const EdgeInsets.only(bottom: 88),
+ children: [
+ SwitchListTile(
+ title: Text(
+ localizations.demoFloatingButtonTitle,
+ ),
+ value: _showFab.value,
+ onChanged: _onShowFabChanged,
+ ),
+ SwitchListTile(
+ title: Text(localizations.bottomAppBarNotch),
+ value: _showNotch.value,
+ onChanged: _onShowNotchChanged,
+ ),
+ Padding(
+ padding: const EdgeInsets.all(16),
+ child: Text(localizations.bottomAppBarPosition),
+ ),
+ RadioListTile(
+ title: Text(
+ localizations.bottomAppBarPositionDockedEnd,
+ ),
+ value: 0,
+ groupValue: _currentFabLocation.value,
+ onChanged: _onFabLocationChanged,
+ ),
+ RadioListTile(
+ title: Text(
+ localizations.bottomAppBarPositionDockedCenter,
+ ),
+ value: 1,
+ groupValue: _currentFabLocation.value,
+ onChanged: _onFabLocationChanged,
+ ),
+ RadioListTile(
+ title: Text(
+ localizations.bottomAppBarPositionFloatingEnd,
+ ),
+ value: 2,
+ groupValue: _currentFabLocation.value,
+ onChanged: _onFabLocationChanged,
+ ),
+ RadioListTile(
+ title: Text(
+ localizations.bottomAppBarPositionFloatingCenter,
+ ),
+ value: 3,
+ groupValue: _currentFabLocation.value,
+ onChanged: _onFabLocationChanged,
+ ),
+ ],
+ ),
+ floatingActionButton: _showFab.value
+ ? Semantics(
+ container: true,
+ sortKey: const OrdinalSortKey(0),
+ child: FloatingActionButton(
+ onPressed: () {},
+ tooltip: localizations.buttonTextCreate,
+ child: const Icon(Icons.add),
+ ),
+ )
+ : null,
+ floatingActionButtonLocation: _fabLocations[_currentFabLocation.value],
+ bottomNavigationBar: _DemoBottomAppBar(
+ fabLocation: _fabLocations[_currentFabLocation.value],
+ shape: _showNotch.value ? const CircularNotchedRectangle() : null,
+ ),
+ );
+ }
+}
+
+class _DemoBottomAppBar extends StatelessWidget {
+ const _DemoBottomAppBar({
+ required this.fabLocation,
+ this.shape,
+ });
+
+ final FloatingActionButtonLocation fabLocation;
+ final NotchedShape? shape;
+
+ static final List centerLocations = [
+ FloatingActionButtonLocation.centerDocked,
+ FloatingActionButtonLocation.centerFloat,
+ ];
+
+ @override
+ Widget build(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ return Semantics(
+ sortKey: const OrdinalSortKey(1),
+ container: true,
+ label: localizations.bottomAppBar,
+ child: BottomAppBar(
+ shape: shape,
+ child: IconTheme(
+ data: IconThemeData(color: Theme.of(context).colorScheme.onPrimary),
+ child: Row(
+ children: [
+ IconButton(
+ tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
+ icon: const Icon(Icons.menu),
+ onPressed: () {},
+ ),
+ if (centerLocations.contains(fabLocation)) const Spacer(),
+ IconButton(
+ tooltip: localizations.starterAppTooltipSearch,
+ icon: const Icon(Icons.search),
+ onPressed: () {},
+ ),
+ IconButton(
+ tooltip: localizations.starterAppTooltipFavorite,
+ icon: const Icon(Icons.favorite),
+ onPressed: () {},
+ )
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
+
+// END
diff --git a/dev/integration_tests/new_gallery/lib/demos/material/bottom_navigation_demo.dart b/dev/integration_tests/new_gallery/lib/demos/material/bottom_navigation_demo.dart
new file mode 100644
index 0000000000..3f272d4e71
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/material/bottom_navigation_demo.dart
@@ -0,0 +1,179 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:animations/animations.dart';
+import 'package:flutter/material.dart';
+import '../../gallery_localizations.dart';
+import 'material_demo_types.dart';
+
+// BEGIN bottomNavigationDemo
+
+class BottomNavigationDemo extends StatefulWidget {
+ const BottomNavigationDemo({
+ super.key,
+ required this.restorationId,
+ required this.type,
+ });
+
+ final String restorationId;
+ final BottomNavigationDemoType type;
+
+ @override
+ State createState() => _BottomNavigationDemoState();
+}
+
+class _BottomNavigationDemoState extends State
+ with RestorationMixin {
+ final RestorableInt _currentIndex = RestorableInt(0);
+
+ @override
+ String get restorationId => widget.restorationId;
+
+ @override
+ void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
+ registerForRestoration(_currentIndex, 'bottom_navigation_tab_index');
+ }
+
+ @override
+ void dispose() {
+ _currentIndex.dispose();
+ super.dispose();
+ }
+
+ String _title(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ switch (widget.type) {
+ case BottomNavigationDemoType.withLabels:
+ return localizations.demoBottomNavigationPersistentLabels;
+ case BottomNavigationDemoType.withoutLabels:
+ return localizations.demoBottomNavigationSelectedLabel;
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final ColorScheme colorScheme = Theme.of(context).colorScheme;
+ final TextTheme textTheme = Theme.of(context).textTheme;
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+
+ List bottomNavigationBarItems = [
+ BottomNavigationBarItem(
+ icon: const Icon(Icons.add_comment),
+ label: localizations.bottomNavigationCommentsTab,
+ ),
+ BottomNavigationBarItem(
+ icon: const Icon(Icons.calendar_today),
+ label: localizations.bottomNavigationCalendarTab,
+ ),
+ BottomNavigationBarItem(
+ icon: const Icon(Icons.account_circle),
+ label: localizations.bottomNavigationAccountTab,
+ ),
+ BottomNavigationBarItem(
+ icon: const Icon(Icons.alarm_on),
+ label: localizations.bottomNavigationAlarmTab,
+ ),
+ BottomNavigationBarItem(
+ icon: const Icon(Icons.camera_enhance),
+ label: localizations.bottomNavigationCameraTab,
+ ),
+ ];
+
+ if (widget.type == BottomNavigationDemoType.withLabels) {
+ bottomNavigationBarItems = bottomNavigationBarItems.sublist(
+ 0, bottomNavigationBarItems.length - 2);
+ _currentIndex.value = _currentIndex.value
+ .clamp(0, bottomNavigationBarItems.length - 1)
+ ;
+ }
+
+ return Scaffold(
+ appBar: AppBar(
+ automaticallyImplyLeading: false,
+ title: Text(_title(context)),
+ ),
+ body: Center(
+ child: PageTransitionSwitcher(
+ transitionBuilder: (Widget child, Animation animation, Animation secondaryAnimation) {
+ return FadeThroughTransition(
+ animation: animation,
+ secondaryAnimation: secondaryAnimation,
+ child: child,
+ );
+ },
+ child: _NavigationDestinationView(
+ // Adding [UniqueKey] to make sure the widget rebuilds when transitioning.
+ key: UniqueKey(),
+ item: bottomNavigationBarItems[_currentIndex.value],
+ ),
+ ),
+ ),
+ bottomNavigationBar: BottomNavigationBar(
+ showUnselectedLabels:
+ widget.type == BottomNavigationDemoType.withLabels,
+ items: bottomNavigationBarItems,
+ currentIndex: _currentIndex.value,
+ type: BottomNavigationBarType.fixed,
+ selectedFontSize: textTheme.bodySmall!.fontSize!,
+ unselectedFontSize: textTheme.bodySmall!.fontSize!,
+ onTap: (int index) {
+ setState(() {
+ _currentIndex.value = index;
+ });
+ },
+ selectedItemColor: colorScheme.onPrimary,
+ unselectedItemColor: colorScheme.onPrimary.withOpacity(0.38),
+ backgroundColor: colorScheme.primary,
+ ),
+ );
+ }
+}
+
+class _NavigationDestinationView extends StatelessWidget {
+ const _NavigationDestinationView({
+ super.key,
+ required this.item,
+ });
+
+ final BottomNavigationBarItem item;
+
+ @override
+ Widget build(BuildContext context) {
+ return Stack(
+ children: [
+ ExcludeSemantics(
+ child: Center(
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: ClipRRect(
+ borderRadius: BorderRadius.circular(8),
+ child: Image.asset(
+ 'assets/demos/bottom_navigation_background.png',
+ package: 'flutter_gallery_assets',
+ ),
+ ),
+ ),
+ ),
+ ),
+ Center(
+ child: IconTheme(
+ data: const IconThemeData(
+ color: Colors.white,
+ size: 80,
+ ),
+ child: Semantics(
+ label: GalleryLocalizations.of(context)!
+ .bottomNavigationContentPlaceholder(
+ item.label!,
+ ),
+ child: item.icon,
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+}
+
+// END
diff --git a/dev/integration_tests/new_gallery/lib/demos/material/bottom_sheet_demo.dart b/dev/integration_tests/new_gallery/lib/demos/material/bottom_sheet_demo.dart
new file mode 100644
index 0000000000..5b029822e1
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/material/bottom_sheet_demo.dart
@@ -0,0 +1,189 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+
+import '../../gallery_localizations.dart';
+import 'material_demo_types.dart';
+
+class BottomSheetDemo extends StatelessWidget {
+ const BottomSheetDemo({
+ super.key,
+ required this.type,
+ });
+
+ final BottomSheetDemoType type;
+
+ String _title(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ switch (type) {
+ case BottomSheetDemoType.persistent:
+ return localizations.demoBottomSheetPersistentTitle;
+ case BottomSheetDemoType.modal:
+ return localizations.demoBottomSheetModalTitle;
+ }
+ }
+
+ Widget _bottomSheetDemo(BuildContext context) {
+ switch (type) {
+ case BottomSheetDemoType.persistent:
+ return _PersistentBottomSheetDemo();
+ case BottomSheetDemoType.modal:
+ return _ModalBottomSheetDemo();
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ // We wrap the demo in a [Navigator] to make sure that the modal bottom
+ // sheets gets dismissed when changing demo.
+ return Navigator(
+ // Adding [ValueKey] to make sure that the widget gets rebuilt when
+ // changing type.
+ key: ValueKey(type),
+ onGenerateRoute: (RouteSettings settings) {
+ return MaterialPageRoute(
+ builder: (BuildContext context) => Scaffold(
+ appBar: AppBar(
+ automaticallyImplyLeading: false,
+ title: Text(_title(context)),
+ ),
+ floatingActionButton: FloatingActionButton(
+ onPressed: () {},
+ backgroundColor: Theme.of(context).colorScheme.secondary,
+ child: Icon(
+ Icons.add,
+ semanticLabel:
+ GalleryLocalizations.of(context)!.demoBottomSheetAddLabel,
+ ),
+ ),
+ body: _bottomSheetDemo(context),
+ ),
+ );
+ },
+ );
+ }
+}
+
+// BEGIN bottomSheetDemoModal#1 bottomSheetDemoPersistent#1
+
+class _BottomSheetContent extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ return SizedBox(
+ height: 300,
+ child: Column(
+ children: [
+ SizedBox(
+ height: 70,
+ child: Center(
+ child: Text(
+ localizations.demoBottomSheetHeader,
+ textAlign: TextAlign.center,
+ ),
+ ),
+ ),
+ const Divider(thickness: 1),
+ Expanded(
+ child: ListView.builder(
+ itemCount: 21,
+ itemBuilder: (BuildContext context, int index) {
+ return ListTile(
+ title: Text(localizations.demoBottomSheetItem(index)),
+ );
+ },
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+// END bottomSheetDemoModal#1 bottomSheetDemoPersistent#1
+
+// BEGIN bottomSheetDemoModal#2
+
+class _ModalBottomSheetDemo extends StatelessWidget {
+ void _showModalBottomSheet(BuildContext context) {
+ showModalBottomSheet(
+ context: context,
+ builder: (BuildContext context) {
+ return _BottomSheetContent();
+ },
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Center(
+ child: ElevatedButton(
+ onPressed: () {
+ _showModalBottomSheet(context);
+ },
+ child:
+ Text(GalleryLocalizations.of(context)!.demoBottomSheetButtonText),
+ ),
+ );
+ }
+}
+
+// END
+
+// BEGIN bottomSheetDemoPersistent#2
+
+class _PersistentBottomSheetDemo extends StatefulWidget {
+ @override
+ _PersistentBottomSheetDemoState createState() =>
+ _PersistentBottomSheetDemoState();
+}
+
+class _PersistentBottomSheetDemoState
+ extends State<_PersistentBottomSheetDemo> {
+ VoidCallback? _showBottomSheetCallback;
+
+ @override
+ void initState() {
+ super.initState();
+ _showBottomSheetCallback = _showPersistentBottomSheet;
+ }
+
+ void _showPersistentBottomSheet() {
+ setState(() {
+ // Disable the show bottom sheet button.
+ _showBottomSheetCallback = null;
+ });
+
+ Scaffold.of(context)
+ .showBottomSheet(
+ (BuildContext context) {
+ return _BottomSheetContent();
+ },
+ elevation: 25,
+ )
+ .closed
+ .whenComplete(() {
+ if (mounted) {
+ setState(() {
+ // Re-enable the bottom sheet button.
+ _showBottomSheetCallback = _showPersistentBottomSheet;
+ });
+ }
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Center(
+ child: ElevatedButton(
+ onPressed: _showBottomSheetCallback,
+ child:
+ Text(GalleryLocalizations.of(context)!.demoBottomSheetButtonText),
+ ),
+ );
+ }
+}
+
+// END
diff --git a/dev/integration_tests/new_gallery/lib/demos/material/button_demo.dart b/dev/integration_tests/new_gallery/lib/demos/material/button_demo.dart
new file mode 100644
index 0000000000..9755b6f816
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/material/button_demo.dart
@@ -0,0 +1,297 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import '../../gallery_localizations.dart';
+import 'material_demo_types.dart';
+
+class ButtonDemo extends StatelessWidget {
+ const ButtonDemo({super.key, required this.type});
+
+ final ButtonDemoType type;
+
+ String _title(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ switch (type) {
+ case ButtonDemoType.text:
+ return localizations.demoTextButtonTitle;
+ case ButtonDemoType.elevated:
+ return localizations.demoElevatedButtonTitle;
+ case ButtonDemoType.outlined:
+ return localizations.demoOutlinedButtonTitle;
+ case ButtonDemoType.toggle:
+ return localizations.demoToggleButtonTitle;
+ case ButtonDemoType.floating:
+ return localizations.demoFloatingButtonTitle;
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ Widget? buttons;
+ switch (type) {
+ case ButtonDemoType.text:
+ buttons = _TextButtonDemo();
+ case ButtonDemoType.elevated:
+ buttons = _ElevatedButtonDemo();
+ case ButtonDemoType.outlined:
+ buttons = _OutlinedButtonDemo();
+ case ButtonDemoType.toggle:
+ buttons = _ToggleButtonsDemo();
+ case ButtonDemoType.floating:
+ buttons = _FloatingActionButtonDemo();
+ }
+
+ return Scaffold(
+ appBar: AppBar(
+ automaticallyImplyLeading: false,
+ title: Text(_title(context)),
+ ),
+ body: buttons,
+ );
+ }
+}
+
+// BEGIN buttonDemoText
+
+class _TextButtonDemo extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ TextButton(
+ onPressed: () {},
+ child: Text(localizations.buttonText),
+ ),
+ const SizedBox(width: 12),
+ TextButton.icon(
+ icon: const Icon(Icons.add, size: 18),
+ label: Text(localizations.buttonText),
+ onPressed: () {},
+ ),
+ ],
+ ),
+ const SizedBox(height: 12),
+ // Disabled buttons
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ TextButton(
+ onPressed: null,
+ child: Text(localizations.buttonText),
+ ),
+ const SizedBox(width: 12),
+ TextButton.icon(
+ icon: const Icon(Icons.add, size: 18),
+ label: Text(localizations.buttonText),
+ onPressed: null,
+ ),
+ ],
+ ),
+ ],
+ );
+ }
+}
+
+// END
+
+// BEGIN buttonDemoElevated
+
+class _ElevatedButtonDemo extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ ElevatedButton(
+ onPressed: () {},
+ child: Text(localizations.buttonText),
+ ),
+ const SizedBox(width: 12),
+ ElevatedButton.icon(
+ icon: const Icon(Icons.add, size: 18),
+ label: Text(localizations.buttonText),
+ onPressed: () {},
+ ),
+ ],
+ ),
+ const SizedBox(height: 12),
+ // Disabled buttons
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ ElevatedButton(
+ onPressed: null,
+ child: Text(localizations.buttonText),
+ ),
+ const SizedBox(width: 12),
+ ElevatedButton.icon(
+ icon: const Icon(Icons.add, size: 18),
+ label: Text(localizations.buttonText),
+ onPressed: null,
+ ),
+ ],
+ ),
+ ],
+ );
+ }
+}
+
+// END
+
+// BEGIN buttonDemoOutlined
+
+class _OutlinedButtonDemo extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ OutlinedButton(
+ onPressed: () {},
+ child: Text(localizations.buttonText),
+ ),
+ const SizedBox(width: 12),
+ OutlinedButton.icon(
+ icon: const Icon(Icons.add, size: 18),
+ label: Text(localizations.buttonText),
+ onPressed: () {},
+ ),
+ ],
+ ),
+ const SizedBox(height: 12),
+ // Disabled buttons
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ OutlinedButton(
+ onPressed: null,
+ child: Text(localizations.buttonText),
+ ),
+ const SizedBox(width: 12),
+ OutlinedButton.icon(
+ icon: const Icon(Icons.add, size: 18),
+ label: Text(localizations.buttonText),
+ onPressed: null,
+ ),
+ ],
+ ),
+ ],
+ );
+ }
+}
+
+// END
+
+// BEGIN buttonDemoToggle
+
+class _ToggleButtonsDemo extends StatefulWidget {
+ @override
+ _ToggleButtonsDemoState createState() => _ToggleButtonsDemoState();
+}
+
+class _ToggleButtonsDemoState extends State<_ToggleButtonsDemo>
+ with RestorationMixin {
+ final List isSelected = [
+ RestorableBool(false),
+ RestorableBool(true),
+ RestorableBool(false),
+ ];
+
+ @override
+ String get restorationId => 'toggle_button_demo';
+
+ @override
+ void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
+ registerForRestoration(isSelected[0], 'first_item');
+ registerForRestoration(isSelected[1], 'second_item');
+ registerForRestoration(isSelected[2], 'third_item');
+ }
+
+ @override
+ void dispose() {
+ for (final RestorableBool restorableBool in isSelected) {
+ restorableBool.dispose();
+ }
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ ToggleButtons(
+ onPressed: (int index) {
+ setState(() {
+ isSelected[index].value = !isSelected[index].value;
+ });
+ },
+ isSelected: isSelected.map((RestorableBool element) => element.value).toList(),
+ children: const [
+ Icon(Icons.format_bold),
+ Icon(Icons.format_italic),
+ Icon(Icons.format_underline),
+ ],
+ ),
+ const SizedBox(height: 12),
+ // Disabled toggle buttons
+ ToggleButtons(
+ isSelected: isSelected.map((RestorableBool element) => element.value).toList(),
+ children: const [
+ Icon(Icons.format_bold),
+ Icon(Icons.format_italic),
+ Icon(Icons.format_underline),
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+// END
+
+// BEGIN buttonDemoFloating
+
+class _FloatingActionButtonDemo extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ return Center(
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ FloatingActionButton(
+ onPressed: () {},
+ tooltip: localizations.buttonTextCreate,
+ child: const Icon(Icons.add),
+ ),
+ const SizedBox(width: 12),
+ FloatingActionButton.extended(
+ icon: const Icon(Icons.add),
+ label: Text(localizations.buttonTextCreate),
+ onPressed: () {},
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+// END
diff --git a/dev/integration_tests/new_gallery/lib/demos/material/cards_demo.dart b/dev/integration_tests/new_gallery/lib/demos/material/cards_demo.dart
new file mode 100644
index 0000000000..1bea3b337e
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/material/cards_demo.dart
@@ -0,0 +1,441 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import '../../gallery_localizations.dart';
+
+const String _kGalleryAssetsPackage = 'flutter_gallery_assets';
+
+// BEGIN cardsDemo
+
+enum CardType {
+ standard,
+ tappable,
+ selectable,
+}
+
+class TravelDestination {
+ const TravelDestination({
+ required this.assetName,
+ required this.assetPackage,
+ required this.title,
+ required this.description,
+ required this.city,
+ required this.location,
+ this.cardType = CardType.standard,
+ });
+
+ final String assetName;
+ final String assetPackage;
+ final String title;
+ final String description;
+ final String city;
+ final String location;
+ final CardType cardType;
+}
+
+List destinations(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+
+ return [
+ TravelDestination(
+ assetName: 'places/india_thanjavur_market.png',
+ assetPackage: _kGalleryAssetsPackage,
+ title: localizations.cardsDemoTravelDestinationTitle1,
+ description: localizations.cardsDemoTravelDestinationDescription1,
+ city: localizations.cardsDemoTravelDestinationCity1,
+ location: localizations.cardsDemoTravelDestinationLocation1,
+ ),
+ TravelDestination(
+ assetName: 'places/india_chettinad_silk_maker.png',
+ assetPackage: _kGalleryAssetsPackage,
+ title: localizations.cardsDemoTravelDestinationTitle2,
+ description: localizations.cardsDemoTravelDestinationDescription2,
+ city: localizations.cardsDemoTravelDestinationCity2,
+ location: localizations.cardsDemoTravelDestinationLocation2,
+ cardType: CardType.tappable,
+ ),
+ TravelDestination(
+ assetName: 'places/india_tanjore_thanjavur_temple.png',
+ assetPackage: _kGalleryAssetsPackage,
+ title: localizations.cardsDemoTravelDestinationTitle3,
+ description: localizations.cardsDemoTravelDestinationDescription3,
+ city: localizations.cardsDemoTravelDestinationCity1,
+ location: localizations.cardsDemoTravelDestinationLocation1,
+ cardType: CardType.selectable,
+ ),
+ ];
+}
+
+class TravelDestinationItem extends StatelessWidget {
+ const TravelDestinationItem(
+ {super.key, required this.destination, this.shape});
+
+ // This height will allow for all the Card's content to fit comfortably within the card.
+ static const double height = 360.0;
+ final TravelDestination destination;
+ final ShapeBorder? shape;
+
+ @override
+ Widget build(BuildContext context) {
+ return SafeArea(
+ top: false,
+ bottom: false,
+ child: Padding(
+ padding: const EdgeInsets.all(8),
+ child: Column(
+ children: [
+ SectionTitle(
+ title: GalleryLocalizations.of(context)!
+ .settingsTextScalingNormal),
+ SizedBox(
+ height: height,
+ child: Card(
+ // This ensures that the Card's children are clipped correctly.
+ clipBehavior: Clip.antiAlias,
+ shape: shape,
+ child: Semantics(
+ label: destination.title,
+ child: TravelDestinationContent(destination: destination),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
+
+class TappableTravelDestinationItem extends StatelessWidget {
+ const TappableTravelDestinationItem({
+ super.key,
+ required this.destination,
+ this.shape,
+ });
+
+ // This height will allow for all the Card's content to fit comfortably within the card.
+ static const double height = 298.0;
+ final TravelDestination destination;
+ final ShapeBorder? shape;
+
+ @override
+ Widget build(BuildContext context) {
+ return SafeArea(
+ top: false,
+ bottom: false,
+ child: Padding(
+ padding: const EdgeInsets.all(8),
+ child: Column(
+ children: [
+ SectionTitle(
+ title: GalleryLocalizations.of(context)!.cardsDemoTappable),
+ SizedBox(
+ height: height,
+ child: Card(
+ // This ensures that the Card's children (including the ink splash) are clipped correctly.
+ clipBehavior: Clip.antiAlias,
+ shape: shape,
+ child: InkWell(
+ onTap: () {},
+ // Generally, material cards use onSurface with 12% opacity for the pressed state.
+ splashColor:
+ Theme.of(context).colorScheme.onSurface.withOpacity(0.12),
+ // Generally, material cards do not have a highlight overlay.
+ highlightColor: Colors.transparent,
+ child: Semantics(
+ label: destination.title,
+ child: TravelDestinationContent(destination: destination),
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
+
+class SelectableTravelDestinationItem extends StatelessWidget {
+ const SelectableTravelDestinationItem({
+ super.key,
+ required this.destination,
+ required this.isSelected,
+ required this.onSelected,
+ this.shape,
+ });
+
+ final TravelDestination destination;
+ final ShapeBorder? shape;
+ final bool isSelected;
+ final VoidCallback onSelected;
+
+ // This height will allow for all the Card's content to fit comfortably within the card.
+ static const double height = 298.0;
+
+ @override
+ Widget build(BuildContext context) {
+ final ColorScheme colorScheme = Theme.of(context).colorScheme;
+ final String selectedStatus = isSelected
+ ? GalleryLocalizations.of(context)!.selected
+ : GalleryLocalizations.of(context)!.notSelected;
+
+ return SafeArea(
+ top: false,
+ bottom: false,
+ child: Padding(
+ padding: const EdgeInsets.all(8),
+ child: Column(
+ children: [
+ SectionTitle(title: GalleryLocalizations.of(context)!.selectable),
+ SizedBox(
+ height: height,
+ child: Card(
+ // This ensures that the Card's children (including the ink splash) are clipped correctly.
+ clipBehavior: Clip.antiAlias,
+ shape: shape,
+ child: InkWell(
+ onLongPress: () {
+ onSelected();
+ },
+ // Generally, material cards use onSurface with 12% opacity for the pressed state.
+ splashColor: colorScheme.onSurface.withOpacity(0.12),
+ // Generally, material cards do not have a highlight overlay.
+ highlightColor: Colors.transparent,
+ child: Stack(
+ children: [
+ Container(
+ color: isSelected
+ // Generally, material cards use primary with 8% opacity for the selected state.
+ // See: https://material.io/design/interaction/states.html#anatomy
+ ? colorScheme.primary.withOpacity(0.08)
+ : Colors.transparent,
+ ),
+ Semantics(
+ label: '${destination.title}, $selectedStatus',
+ onLongPressHint: isSelected
+ ? GalleryLocalizations.of(context)!.deselect
+ : GalleryLocalizations.of(context)!.select,
+ child:
+ TravelDestinationContent(destination: destination),
+ ),
+ Align(
+ alignment: Alignment.topRight,
+ child: Padding(
+ padding: const EdgeInsets.all(8),
+ child: Icon(
+ Icons.check_circle,
+ color: isSelected
+ ? colorScheme.primary
+ : Colors.transparent,
+ ),
+ ),
+ ),
+ ],
+ ),
+ //),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
+
+class SectionTitle extends StatelessWidget {
+ const SectionTitle({
+ super.key,
+ required this.title,
+ });
+
+ final String title;
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.fromLTRB(4, 4, 4, 12),
+ child: Align(
+ alignment: Alignment.centerLeft,
+ child: Text(title, style: Theme.of(context).textTheme.titleMedium),
+ ),
+ );
+ }
+}
+
+class TravelDestinationContent extends StatelessWidget {
+ const TravelDestinationContent({super.key, required this.destination});
+
+ final TravelDestination destination;
+
+ @override
+ Widget build(BuildContext context) {
+ final ThemeData theme = Theme.of(context);
+ final TextStyle titleStyle = theme.textTheme.headlineSmall!.copyWith(
+ color: Colors.white,
+ );
+ final TextStyle descriptionStyle = theme.textTheme.titleMedium!;
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ SizedBox(
+ height: 184,
+ child: Stack(
+ children: [
+ Positioned.fill(
+ // In order to have the ink splash appear above the image, you
+ // must use Ink.image. This allows the image to be painted as
+ // part of the Material and display ink effects above it. Using
+ // a standard Image will obscure the ink splash.
+ child: Ink.image(
+ image: AssetImage(
+ destination.assetName,
+ package: destination.assetPackage,
+ ),
+ fit: BoxFit.cover,
+ child: Container(),
+ ),
+ ),
+ Positioned(
+ bottom: 16,
+ left: 16,
+ right: 16,
+ child: FittedBox(
+ fit: BoxFit.scaleDown,
+ alignment: Alignment.centerLeft,
+ child: Semantics(
+ container: true,
+ header: true,
+ child: Text(
+ destination.title,
+ style: titleStyle,
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ // Description and share/explore buttons.
+ Semantics(
+ container: true,
+ child: Padding(
+ padding: const EdgeInsets.fromLTRB(16, 16, 16, 0),
+ child: DefaultTextStyle(
+ softWrap: false,
+ overflow: TextOverflow.ellipsis,
+ style: descriptionStyle,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // This array contains the three line description on each card
+ // demo.
+ Padding(
+ padding: const EdgeInsets.only(bottom: 8),
+ child: Text(
+ destination.description,
+ style: descriptionStyle.copyWith(color: Colors.black54),
+ ),
+ ),
+ Text(destination.city),
+ Text(destination.location),
+ ],
+ ),
+ ),
+ ),
+ ),
+ if (destination.cardType == CardType.standard)
+ // share, explore buttons
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: OverflowBar(
+ alignment: MainAxisAlignment.start,
+ spacing: 8,
+ children: [
+ TextButton(
+ onPressed: () {},
+ child: Text(localizations.demoMenuShare,
+ semanticsLabel: localizations
+ .cardsDemoShareSemantics(destination.title)),
+ ),
+ TextButton(
+ onPressed: () {},
+ child: Text(localizations.cardsDemoExplore,
+ semanticsLabel: localizations
+ .cardsDemoExploreSemantics(destination.title)),
+ ),
+ ],
+ ),
+ ),
+ ],
+ );
+ }
+}
+
+class CardsDemo extends StatefulWidget {
+ const CardsDemo({super.key});
+
+ @override
+ State createState() => _CardsDemoState();
+}
+
+class _CardsDemoState extends State with RestorationMixin {
+ final RestorableBool _isSelected = RestorableBool(false);
+
+ @override
+ String get restorationId => 'cards_demo';
+
+ @override
+ void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
+ registerForRestoration(_isSelected, 'is_selected');
+ }
+
+ @override
+ void dispose() {
+ _isSelected.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ automaticallyImplyLeading: false,
+ title: Text(GalleryLocalizations.of(context)!.demoCardTitle),
+ ),
+ body: Scrollbar(
+ child: ListView(
+ restorationId: 'cards_demo_list_view',
+ padding: const EdgeInsets.only(top: 8, left: 8, right: 8),
+ children: [
+ for (final TravelDestination destination in destinations(context))
+ Container(
+ margin: const EdgeInsets.only(bottom: 8),
+ child: (destination.cardType == CardType.standard)
+ ? TravelDestinationItem(destination: destination)
+ : destination.cardType == CardType.tappable
+ ? TappableTravelDestinationItem(
+ destination: destination)
+ : SelectableTravelDestinationItem(
+ destination: destination,
+ isSelected: _isSelected.value,
+ onSelected: () {
+ setState(() {
+ _isSelected.value = !_isSelected.value;
+ });
+ },
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
+
+// END
diff --git a/dev/integration_tests/new_gallery/lib/demos/material/chip_demo.dart b/dev/integration_tests/new_gallery/lib/demos/material/chip_demo.dart
new file mode 100644
index 0000000000..110b5e143c
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/material/chip_demo.dart
@@ -0,0 +1,306 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import '../../gallery_localizations.dart';
+import 'material_demo_types.dart';
+
+class ChipDemo extends StatelessWidget {
+ const ChipDemo({
+ super.key,
+ required this.type,
+ });
+
+ final ChipDemoType type;
+
+ String _title(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ switch (type) {
+ case ChipDemoType.action:
+ return localizations.demoActionChipTitle;
+ case ChipDemoType.choice:
+ return localizations.demoChoiceChipTitle;
+ case ChipDemoType.filter:
+ return localizations.demoFilterChipTitle;
+ case ChipDemoType.input:
+ return localizations.demoInputChipTitle;
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ Widget? buttons;
+ switch (type) {
+ case ChipDemoType.action:
+ buttons = _ActionChipDemo();
+ case ChipDemoType.choice:
+ buttons = _ChoiceChipDemo();
+ case ChipDemoType.filter:
+ buttons = _FilterChipDemo();
+ case ChipDemoType.input:
+ buttons = _InputChipDemo();
+ }
+
+ return Scaffold(
+ appBar: AppBar(
+ automaticallyImplyLeading: false,
+ title: Text(_title(context)),
+ ),
+ body: buttons,
+ );
+ }
+}
+
+// BEGIN chipDemoAction
+
+class _ActionChipDemo extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return Center(
+ child: ActionChip(
+ onPressed: () {},
+ avatar: const Icon(
+ Icons.brightness_5,
+ color: Colors.black54,
+ ),
+ label: Text(GalleryLocalizations.of(context)!.chipTurnOnLights),
+ ),
+ );
+ }
+}
+
+// END
+
+// BEGIN chipDemoChoice
+
+class _ChoiceChipDemo extends StatefulWidget {
+ @override
+ _ChoiceChipDemoState createState() => _ChoiceChipDemoState();
+}
+
+class _ChoiceChipDemoState extends State<_ChoiceChipDemo>
+ with RestorationMixin {
+ final RestorableIntN _indexSelected = RestorableIntN(null);
+
+ @override
+ String get restorationId => 'choice_chip_demo';
+
+ @override
+ void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
+ registerForRestoration(_indexSelected, 'choice_chip');
+ }
+
+ @override
+ void dispose() {
+ _indexSelected.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ return Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Wrap(
+ children: [
+ ChoiceChip(
+ label: Text(localizations.chipSmall),
+ selected: _indexSelected.value == 0,
+ onSelected: (bool value) {
+ setState(() {
+ _indexSelected.value = value ? 0 : -1;
+ });
+ },
+ ),
+ const SizedBox(width: 8),
+ ChoiceChip(
+ label: Text(localizations.chipMedium),
+ selected: _indexSelected.value == 1,
+ onSelected: (bool value) {
+ setState(() {
+ _indexSelected.value = value ? 1 : -1;
+ });
+ },
+ ),
+ const SizedBox(width: 8),
+ ChoiceChip(
+ label: Text(localizations.chipLarge),
+ selected: _indexSelected.value == 2,
+ onSelected: (bool value) {
+ setState(() {
+ _indexSelected.value = value ? 2 : -1;
+ });
+ },
+ ),
+ ],
+ ),
+ const SizedBox(height: 12),
+ // Disabled chips
+ Wrap(
+ children: [
+ ChoiceChip(
+ label: Text(localizations.chipSmall),
+ selected: _indexSelected.value == 0,
+ ),
+ const SizedBox(width: 8),
+ ChoiceChip(
+ label: Text(localizations.chipMedium),
+ selected: _indexSelected.value == 1,
+ ),
+ const SizedBox(width: 8),
+ ChoiceChip(
+ label: Text(localizations.chipLarge),
+ selected: _indexSelected.value == 2,
+ ),
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+// END
+
+// BEGIN chipDemoFilter
+
+class _FilterChipDemo extends StatefulWidget {
+ @override
+ _FilterChipDemoState createState() => _FilterChipDemoState();
+}
+
+class _FilterChipDemoState extends State<_FilterChipDemo>
+ with RestorationMixin {
+ final RestorableBool isSelectedElevator = RestorableBool(false);
+ final RestorableBool isSelectedWasher = RestorableBool(false);
+ final RestorableBool isSelectedFireplace = RestorableBool(false);
+
+ @override
+ String get restorationId => 'filter_chip_demo';
+
+ @override
+ void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
+ registerForRestoration(isSelectedElevator, 'selected_elevator');
+ registerForRestoration(isSelectedWasher, 'selected_washer');
+ registerForRestoration(isSelectedFireplace, 'selected_fireplace');
+ }
+
+ @override
+ void dispose() {
+ isSelectedElevator.dispose();
+ isSelectedWasher.dispose();
+ isSelectedFireplace.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+
+ return Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Wrap(
+ spacing: 8.0,
+ children: [
+ FilterChip(
+ label: Text(localizations.chipElevator),
+ selected: isSelectedElevator.value,
+ onSelected: (bool value) {
+ setState(() {
+ isSelectedElevator.value = !isSelectedElevator.value;
+ });
+ },
+ ),
+ FilterChip(
+ label: Text(localizations.chipWasher),
+ selected: isSelectedWasher.value,
+ onSelected: (bool value) {
+ setState(() {
+ isSelectedWasher.value = !isSelectedWasher.value;
+ });
+ },
+ ),
+ FilterChip(
+ label: Text(localizations.chipFireplace),
+ selected: isSelectedFireplace.value,
+ onSelected: (bool value) {
+ setState(() {
+ isSelectedFireplace.value = !isSelectedFireplace.value;
+ });
+ },
+ ),
+ ],
+ ),
+ const SizedBox(height: 12),
+ // Disabled chips
+ Wrap(
+ spacing: 8.0,
+ children: [
+ FilterChip(
+ label: Text(localizations.chipElevator),
+ selected: isSelectedElevator.value,
+ onSelected: null,
+ ),
+ FilterChip(
+ label: Text(localizations.chipWasher),
+ selected: isSelectedWasher.value,
+ onSelected: null,
+ ),
+ FilterChip(
+ label: Text(localizations.chipFireplace),
+ selected: isSelectedFireplace.value,
+ onSelected: null,
+ ),
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+// END
+
+// BEGIN chipDemoInput
+
+class _InputChipDemo extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ InputChip(
+ onPressed: () {},
+ onDeleted: () {},
+ avatar: const Icon(
+ Icons.directions_bike,
+ size: 20,
+ color: Colors.black54,
+ ),
+ deleteIconColor: Colors.black54,
+ label: Text(GalleryLocalizations.of(context)!.chipBiking),
+ ),
+ const SizedBox(height: 12),
+ // Disabled chip
+ InputChip(
+ avatar: const Icon(
+ Icons.directions_bike,
+ size: 20,
+ color: Colors.black54,
+ ),
+ deleteIconColor: Colors.black54,
+ label: Text(GalleryLocalizations.of(context)!.chipBiking),
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+// END
diff --git a/dev/integration_tests/new_gallery/lib/demos/material/data_table_demo.dart b/dev/integration_tests/new_gallery/lib/demos/material/data_table_demo.dart
new file mode 100644
index 0000000000..d88ca630c4
--- /dev/null
+++ b/dev/integration_tests/new_gallery/lib/demos/material/data_table_demo.dart
@@ -0,0 +1,680 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:intl/intl.dart';
+
+import '../../data/gallery_options.dart';
+import '../../gallery_localizations.dart';
+
+// BEGIN dataTableDemo
+
+class DataTableDemo extends StatefulWidget {
+ const DataTableDemo({super.key});
+
+ @override
+ State createState() => _DataTableDemoState();
+}
+
+class _RestorableDessertSelections extends RestorableProperty> {
+ Set _dessertSelections = {};
+
+ /// Returns whether or not a dessert row is selected by index.
+ bool isSelected(int index) => _dessertSelections.contains(index);
+
+ /// Takes a list of [_Dessert]s and saves the row indices of selected rows
+ /// into a [Set].
+ void setDessertSelections(List<_Dessert> desserts) {
+ final Set updatedSet = {};
+ for (int i = 0; i < desserts.length; i += 1) {
+ final _Dessert dessert = desserts[i];
+ if (dessert.selected) {
+ updatedSet.add(i);
+ }
+ }
+ _dessertSelections = updatedSet;
+ notifyListeners();
+ }
+
+ @override
+ Set createDefaultValue() => _dessertSelections;
+
+ @override
+ Set fromPrimitives(Object? data) {
+ final List selectedItemIndices = data! as List;
+ _dessertSelections = {
+ ...selectedItemIndices.map((dynamic id) => id as int),
+ };
+ return _dessertSelections;
+ }
+
+ @override
+ void initWithValue(Set value) {
+ _dessertSelections = value;
+ }
+
+ @override
+ Object toPrimitives() => _dessertSelections.toList();
+}
+
+class _DataTableDemoState extends State with RestorationMixin {
+ final _RestorableDessertSelections _dessertSelections =
+ _RestorableDessertSelections();
+ final RestorableInt _rowIndex = RestorableInt(0);
+ final RestorableInt _rowsPerPage =
+ RestorableInt(PaginatedDataTable.defaultRowsPerPage);
+ final RestorableBool _sortAscending = RestorableBool(true);
+ final RestorableIntN _sortColumnIndex = RestorableIntN(null);
+ _DessertDataSource? _dessertsDataSource;
+
+ @override
+ String get restorationId => 'data_table_demo';
+
+ @override
+ void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
+ registerForRestoration(_dessertSelections, 'selected_row_indices');
+ registerForRestoration(_rowIndex, 'current_row_index');
+ registerForRestoration(_rowsPerPage, 'rows_per_page');
+ registerForRestoration(_sortAscending, 'sort_ascending');
+ registerForRestoration(_sortColumnIndex, 'sort_column_index');
+
+ _dessertsDataSource ??= _DessertDataSource(context);
+ switch (_sortColumnIndex.value) {
+ case 0:
+ _dessertsDataSource!._sort((_Dessert d) => d.name, _sortAscending.value);
+ case 1:
+ _dessertsDataSource!
+ ._sort((_Dessert d) => d.calories, _sortAscending.value);
+ case 2:
+ _dessertsDataSource!._sort((_Dessert d) => d.fat, _sortAscending.value);
+ case 3:
+ _dessertsDataSource!._sort((_Dessert d) => d.carbs, _sortAscending.value);
+ case 4:
+ _dessertsDataSource!._sort((_Dessert d) => d.protein, _sortAscending.value);
+ case 5:
+ _dessertsDataSource!._sort((_Dessert d) => d.sodium, _sortAscending.value);
+ case 6:
+ _dessertsDataSource!._sort((_Dessert d) => d.calcium, _sortAscending.value);
+ case 7:
+ _dessertsDataSource!._sort((_Dessert d) => d.iron, _sortAscending.value);
+ }
+ _dessertsDataSource!.updateSelectedDesserts(_dessertSelections);
+ _dessertsDataSource!.addListener(_updateSelectedDessertRowListener);
+ }
+
+ @override
+ void didChangeDependencies() {
+ super.didChangeDependencies();
+ _dessertsDataSource ??= _DessertDataSource(context);
+ _dessertsDataSource!.addListener(_updateSelectedDessertRowListener);
+ }
+
+ void _updateSelectedDessertRowListener() {
+ _dessertSelections.setDessertSelections(_dessertsDataSource!._desserts);
+ }
+
+ void _sort(
+ Comparable Function(_Dessert d) getField,
+ int columnIndex,
+ bool ascending,
+ ) {
+ _dessertsDataSource!._sort(getField, ascending);
+ setState(() {
+ _sortColumnIndex.value = columnIndex;
+ _sortAscending.value = ascending;
+ });
+ }
+
+ @override
+ void dispose() {
+ _rowsPerPage.dispose();
+ _sortColumnIndex.dispose();
+ _sortAscending.dispose();
+ _dessertsDataSource!.removeListener(_updateSelectedDessertRowListener);
+ _dessertsDataSource!.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final GalleryLocalizations localizations = GalleryLocalizations.of(context)!;
+ return Scaffold(
+ appBar: AppBar(
+ automaticallyImplyLeading: false,
+ title: Text(localizations.demoDataTableTitle),
+ ),
+ body: Scrollbar(
+ child: ListView(
+ restorationId: 'data_table_list_view',
+ padding: const EdgeInsets.all(16),
+ children: