diff --git a/packages/cupertino_ui/lib/cupertino_ui.dart b/packages/cupertino_ui/lib/cupertino_ui.dart index 698aaf04a5cc..c951ace9dda8 100644 --- a/packages/cupertino_ui/lib/cupertino_ui.dart +++ b/packages/cupertino_ui/lib/cupertino_ui.dart @@ -55,6 +55,7 @@ export 'src/list_tile.dart'; export 'src/localizations.dart'; export 'src/magnifier.dart'; export 'src/menu_anchor.dart'; +export 'src/migration_utility.dart'; export 'src/nav_bar.dart'; export 'src/page_scaffold.dart'; export 'src/picker.dart'; diff --git a/packages/cupertino_ui/lib/src/migration_utility.dart b/packages/cupertino_ui/lib/src/migration_utility.dart new file mode 100644 index 000000000000..47639f55bf53 --- /dev/null +++ b/packages/cupertino_ui/lib/src/migration_utility.dart @@ -0,0 +1,136 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:cupertino_ui/cupertino_ui.dart' as modern; // ignore: prefer_relative_imports +import 'package:flutter/cupertino.dart' as legacy; +import 'package:flutter/widgets.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; + +/// A bridge widget for applications that have migrated to `package:cupertino_ui` +/// but still contain subtrees or package dependencies using `package:flutter/cupertino.dart`. +/// +/// This widget maps the parent [modern.CupertinoThemeData] from `package:cupertino_ui` into +/// a legacy [legacy.CupertinoThemeData] and overrides [legacy.Localizations] with delegates +/// so that legacy widgets in [child] can resolve `legacy.CupertinoTheme.of(context)` and +/// `legacy.CupertinoLocalizations.of(context)`. +/// +/// ## App-wide usage +/// +/// The recommended way to use this bridge across an entire application is to +/// wrap the app once using [modern.CupertinoApp.builder]: +/// +/// ```dart +/// import 'package:cupertino_ui/cupertino_ui.dart'; +/// +/// void main() { +/// runApp(const MyApp()); +/// } +/// +/// class MyApp extends StatelessWidget { +/// const MyApp({super.key}); +/// +/// @override +/// Widget build(BuildContext context) { +/// return CupertinoApp( +/// theme: const CupertinoThemeData( +/// primaryColor: CupertinoColors.systemIndigo, +/// ), +/// builder: (BuildContext context, Widget? child) { +/// return CupertinoUiCompatibilityBridge(child: child!); +/// }, +/// home: const HomeScreen(), +/// ); +/// } +/// } +/// ``` +/// +/// ## Subtree usage +/// +/// Alternatively, wrap specific legacy package widgets or subtrees: +/// +/// ```dart +/// import 'package:cupertino_ui/cupertino_ui.dart'; +/// import 'package:some_legacy_package/some_legacy_package.dart'; +/// +/// class MyScreen extends StatelessWidget { +/// const MyScreen({super.key}); +/// +/// @override +/// Widget build(BuildContext context) { +/// return CupertinoPageScaffold( +/// navigationBar: const CupertinoNavigationBar( +/// middle: Text('Migrated Screen'), +/// ), +/// child: CupertinoUiCompatibilityBridge( +/// child: LegacyPackageWidget(), +/// ), +/// ); +/// } +/// } +/// ``` +@Deprecated( + 'CupertinoUiCompatibilityBridge is a temporary migration utility intended for use only while ' + 'dependencies are being migrated to package:cupertino_ui. ' + 'This feature was deprecated to indicate it will be removed in a future release.', +) +class CupertinoUiCompatibilityBridge extends StatelessWidget { + /// Creates a [CupertinoUiCompatibilityBridge]. + @Deprecated( + 'CupertinoUiCompatibilityBridge is a temporary migration utility intended for use only while ' + 'dependencies are being migrated to package:cupertino_ui. ' + 'This feature was deprecated to indicate it will be removed in a future release.', + ) + const CupertinoUiCompatibilityBridge({super.key, required this.child, this.delegates}); + + /// The widget below this widget in the tree. + final Widget child; + + /// The localizations delegates for legacy widgets. + /// + /// Defaults to `[GlobalCupertinoLocalizations.delegate, GlobalWidgetsLocalizations.delegate]`. + final List>? delegates; + + @override + Widget build(BuildContext context) { + final modern.CupertinoThemeData modernTheme = modern.CupertinoTheme.of(context); + + return legacy.CupertinoTheme( + data: _mapToLegacy(modernTheme), + child: legacy.Localizations.override( + context: context, + delegates: + delegates ?? + const >[ + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + child: child, + ), + ); + } + + legacy.CupertinoThemeData _mapToLegacy(modern.CupertinoThemeData modernTheme) { + return legacy.CupertinoThemeData( + brightness: modernTheme.brightness, + primaryColor: modernTheme.primaryColor, + primaryContrastingColor: modernTheme.primaryContrastingColor, + barBackgroundColor: modernTheme.barBackgroundColor, + scaffoldBackgroundColor: modernTheme.scaffoldBackgroundColor, + selectionHandleColor: modernTheme.selectionHandleColor, + applyThemeToAll: modernTheme.applyThemeToAll, + textTheme: legacy.CupertinoTextThemeData( + primaryColor: modernTheme.primaryColor, + textStyle: modernTheme.textTheme.textStyle, + actionTextStyle: modernTheme.textTheme.actionTextStyle, + actionSmallTextStyle: modernTheme.textTheme.actionSmallTextStyle, + tabLabelTextStyle: modernTheme.textTheme.tabLabelTextStyle, + navTitleTextStyle: modernTheme.textTheme.navTitleTextStyle, + navLargeTitleTextStyle: modernTheme.textTheme.navLargeTitleTextStyle, + navActionTextStyle: modernTheme.textTheme.navActionTextStyle, + pickerTextStyle: modernTheme.textTheme.pickerTextStyle, + dateTimePickerTextStyle: modernTheme.textTheme.dateTimePickerTextStyle, + ), + ); + } +} diff --git a/packages/cupertino_ui/pending_changelogs/change_2026_07_29_compatibility_bridge.yaml b/packages/cupertino_ui/pending_changelogs/change_2026_07_29_compatibility_bridge.yaml new file mode 100644 index 000000000000..6510f17c30f9 --- /dev/null +++ b/packages/cupertino_ui/pending_changelogs/change_2026_07_29_compatibility_bridge.yaml @@ -0,0 +1,3 @@ +changelog: | + - Adds CupertinoUiCompatibilityBridge for legacy flutter/cupertino.dart compatibility. +version: minor diff --git a/packages/cupertino_ui/test/migration_utility_test.dart b/packages/cupertino_ui/test/migration_utility_test.dart new file mode 100644 index 000000000000..c5921f03f10d --- /dev/null +++ b/packages/cupertino_ui/test/migration_utility_test.dart @@ -0,0 +1,182 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ignore_for_file: deprecated_member_use + +import 'package:cupertino_ui/cupertino_ui.dart' as modern; +import 'package:flutter/cupertino.dart' as legacy; +import 'package:flutter/widgets.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('CupertinoUiCompatibilityBridge provides mapped CupertinoThemeData properties', ( + WidgetTester tester, + ) async { + legacy.CupertinoThemeData? capturedLegacyTheme; + + const modernThemeData = modern.CupertinoThemeData( + brightness: Brightness.dark, + primaryColor: Color(0xFF00FF00), + primaryContrastingColor: Color(0xFFFF0000), + barBackgroundColor: Color(0xFF111111), + scaffoldBackgroundColor: Color(0xFF222222), + ); + + await tester.pumpWidget( + modern.CupertinoApp( + theme: modernThemeData, + home: modern.CupertinoUiCompatibilityBridge( + child: Builder( + builder: (BuildContext context) { + capturedLegacyTheme = legacy.CupertinoTheme.of(context); + return const SizedBox.shrink(); + }, + ), + ), + ), + ); + + expect(capturedLegacyTheme, isNotNull); + expect(capturedLegacyTheme!.brightness, modernThemeData.brightness); + expect(capturedLegacyTheme!.primaryColor, modernThemeData.primaryColor); + expect(capturedLegacyTheme!.primaryContrastingColor, modernThemeData.primaryContrastingColor); + expect(capturedLegacyTheme!.barBackgroundColor, modernThemeData.barBackgroundColor); + expect(capturedLegacyTheme!.scaffoldBackgroundColor, modernThemeData.scaffoldBackgroundColor); + }); + + testWidgets('CupertinoUiCompatibilityBridge maps CupertinoTextThemeData properties', ( + WidgetTester tester, + ) async { + legacy.CupertinoThemeData? capturedLegacyTheme; + + const modernTextTheme = modern.CupertinoTextThemeData( + navTitleTextStyle: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold), + actionTextStyle: TextStyle(fontSize: 16.0, color: Color(0xFF123456)), + ); + + const modernThemeData = modern.CupertinoThemeData(textTheme: modernTextTheme); + + await tester.pumpWidget( + modern.CupertinoApp( + theme: modernThemeData, + home: modern.CupertinoUiCompatibilityBridge( + child: Builder( + builder: (BuildContext context) { + capturedLegacyTheme = legacy.CupertinoTheme.of(context); + return const SizedBox.shrink(); + }, + ), + ), + ), + ); + + expect(capturedLegacyTheme, isNotNull); + expect( + capturedLegacyTheme!.textTheme.navTitleTextStyle.fontSize, + modernTextTheme.navTitleTextStyle.fontSize, + ); + expect( + capturedLegacyTheme!.textTheme.navTitleTextStyle.fontWeight, + modernTextTheme.navTitleTextStyle.fontWeight, + ); + expect( + capturedLegacyTheme!.textTheme.actionTextStyle.color, + modernTextTheme.actionTextStyle.color, + ); + }); + + testWidgets('CupertinoUiCompatibilityBridge provides CupertinoLocalizations', ( + WidgetTester tester, + ) async { + legacy.CupertinoLocalizations? capturedLegacyLocalizations; + + await tester.pumpWidget( + modern.CupertinoApp( + home: modern.CupertinoUiCompatibilityBridge( + child: Builder( + builder: (BuildContext context) { + capturedLegacyLocalizations = legacy.CupertinoLocalizations.of(context); + return const SizedBox.shrink(); + }, + ), + ), + ), + ); + + expect(capturedLegacyLocalizations, isNotNull); + expect(capturedLegacyLocalizations!.datePickerDateOrder, isNotNull); + expect(capturedLegacyLocalizations!.modalBarrierDismissLabel, isNotEmpty); + }); + + testWidgets('CupertinoUiCompatibilityBridge allows rendering legacy Cupertino widgets', ( + WidgetTester tester, + ) async { + await tester.pumpWidget( + modern.CupertinoApp( + home: modern.CupertinoUiCompatibilityBridge( + child: legacy.CupertinoPageScaffold( + navigationBar: const legacy.CupertinoNavigationBar(middle: Text('Legacy Title')), + child: Center( + child: legacy.CupertinoButton(onPressed: () {}, child: const Text('Legacy Button')), + ), + ), + ), + ), + ); + + expect(find.text('Legacy Title'), findsOneWidget); + expect(find.text('Legacy Button'), findsOneWidget); + expect(find.byType(legacy.CupertinoButton), findsOneWidget); + expect(find.byType(legacy.CupertinoPageScaffold), findsOneWidget); + }); + + testWidgets('CupertinoUiCompatibilityBridge supports custom delegates', ( + WidgetTester tester, + ) async { + legacy.CupertinoLocalizations? capturedLegacyLocalizations; + + await tester.pumpWidget( + modern.CupertinoApp( + home: modern.CupertinoUiCompatibilityBridge( + delegates: const >[GlobalCupertinoLocalizations.delegate], + child: Builder( + builder: (BuildContext context) { + capturedLegacyLocalizations = legacy.CupertinoLocalizations.of(context); + return const SizedBox.shrink(); + }, + ), + ), + ), + ); + + expect(capturedLegacyLocalizations, isNotNull); + expect(capturedLegacyLocalizations!.modalBarrierDismissLabel, isNotEmpty); + }); + + testWidgets('CupertinoUiCompatibilityBridge works at root in CupertinoApp.builder', ( + WidgetTester tester, + ) async { + await tester.pumpWidget( + modern.CupertinoApp( + builder: (BuildContext context, Widget? child) { + return modern.CupertinoUiCompatibilityBridge(child: child!); + }, + home: modern.CupertinoPageScaffold( + child: Column( + children: [ + modern.CupertinoButton(onPressed: () {}, child: const Text('Modern Button')), + legacy.CupertinoButton(onPressed: () {}, child: const Text('Legacy Button')), + ], + ), + ), + ), + ); + + expect(find.text('Modern Button'), findsOneWidget); + expect(find.text('Legacy Button'), findsOneWidget); + expect(find.byType(modern.CupertinoButton), findsOneWidget); + expect(find.byType(legacy.CupertinoButton), findsOneWidget); + }); +} diff --git a/packages/material_ui/lib/material_ui.dart b/packages/material_ui/lib/material_ui.dart index 92dd582432fa..a4ac72616e3d 100644 --- a/packages/material_ui/lib/material_ui.dart +++ b/packages/material_ui/lib/material_ui.dart @@ -129,6 +129,7 @@ export 'src/menu_button_theme.dart'; export 'src/menu_style.dart'; export 'src/menu_theme.dart'; export 'src/mergeable_material.dart'; +export 'src/migration_utility.dart'; export 'src/motion.dart'; export 'src/navigation_bar.dart'; export 'src/navigation_bar_theme.dart'; diff --git a/packages/material_ui/lib/src/migration_utility.dart b/packages/material_ui/lib/src/migration_utility.dart new file mode 100644 index 000000000000..4428c78d5231 --- /dev/null +++ b/packages/material_ui/lib/src/migration_utility.dart @@ -0,0 +1,178 @@ +// Copyright 2013 The Flutter Authors +// 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' as legacy; +import 'package:flutter/widgets.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:material_ui/material_ui.dart' as modern; // ignore: prefer_relative_imports + +/// A bridge widget for applications that have migrated to `package:material_ui` +/// but still contain subtrees or package dependencies using `package:flutter/material.dart`. +/// +/// This widget maps the parent [modern.ThemeData] from `package:material_ui` into +/// a legacy [legacy.ThemeData] and overrides [legacy.Localizations] with delegates +/// so that legacy widgets in [child] can resolve `legacy.Theme.of(context)` and +/// `legacy.MaterialLocalizations.of(context)`. +/// +/// ## App-wide usage +/// +/// The recommended way to use this bridge across an entire application is to +/// wrap the app once using [modern.MaterialApp.builder]: +/// +/// ```dart +/// import 'package:material_ui/material_ui.dart'; +/// +/// void main() { +/// runApp(const MyApp()); +/// } +/// +/// class MyApp extends StatelessWidget { +/// const MyApp({super.key}); +/// +/// @override +/// Widget build(BuildContext context) { +/// return MaterialApp( +/// theme: ThemeData( +/// colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF6750A4)), +/// ), +/// builder: (BuildContext context, Widget? child) { +/// return MaterialUiCompatibilityBridge(child: child!); +/// }, +/// home: const HomeScreen(), +/// ); +/// } +/// } +/// ``` +/// +/// ## Subtree usage +/// +/// Alternatively, wrap specific legacy package widgets or subtrees: +/// +/// ```dart +/// import 'package:material_ui/material_ui.dart'; +/// import 'package:some_legacy_package/some_legacy_package.dart'; +/// +/// class MyScreen extends StatelessWidget { +/// const MyScreen({super.key}); +/// +/// @override +/// Widget build(BuildContext context) { +/// return Scaffold( +/// appBar: AppBar(title: const Text('Migrated Screen')), +/// body: MaterialUiCompatibilityBridge( +/// child: LegacyPackageWidget(), +/// ), +/// ); +/// } +/// } +/// ``` +@Deprecated( + 'MaterialUiCompatibilityBridge is a temporary migration utility intended for use only while ' + 'dependencies are being migrated to package:material_ui. ' + 'This feature was deprecated to indicate it will be removed in a future release.', +) +class MaterialUiCompatibilityBridge extends StatelessWidget { + /// Creates a [MaterialUiCompatibilityBridge]. + @Deprecated( + 'MaterialUiCompatibilityBridge is a temporary migration utility intended for use only while ' + 'dependencies are being migrated to package:material_ui. ' + 'This feature was deprecated to indicate it will be removed in a future release.', + ) + const MaterialUiCompatibilityBridge({super.key, required this.child, this.delegates}); + + /// The widget below this widget in the tree. + final Widget child; + + /// The localizations delegates for legacy widgets. + /// + /// Defaults to `[GlobalCupertinoLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate]`. + final List>? delegates; + + @override + Widget build(BuildContext context) { + final modern.ThemeData modernTheme = modern.Theme.of(context); + + return legacy.Theme( + data: _mapToLegacy(modernTheme), + child: legacy.Localizations.override( + context: context, + delegates: + delegates ?? + const >[ + GlobalCupertinoLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + child: child, + ), + ); + } + + legacy.ThemeData _mapToLegacy(modern.ThemeData modernTheme) { + final modern.ColorScheme scheme = modernTheme.colorScheme; + final modern.TextTheme textTheme = modernTheme.textTheme; + + return legacy.ThemeData( + platform: modernTheme.platform, + visualDensity: legacy.VisualDensity( + horizontal: modernTheme.visualDensity.horizontal, + vertical: modernTheme.visualDensity.vertical, + ), + colorScheme: legacy.ColorScheme( + brightness: scheme.brightness, + primary: scheme.primary, + onPrimary: scheme.onPrimary, + primaryContainer: scheme.primaryContainer, + onPrimaryContainer: scheme.onPrimaryContainer, + secondary: scheme.secondary, + onSecondary: scheme.onSecondary, + secondaryContainer: scheme.secondaryContainer, + onSecondaryContainer: scheme.onSecondaryContainer, + tertiary: scheme.tertiary, + onTertiary: scheme.onTertiary, + tertiaryContainer: scheme.tertiaryContainer, + onTertiaryContainer: scheme.onTertiaryContainer, + error: scheme.error, + onError: scheme.onError, + errorContainer: scheme.errorContainer, + onErrorContainer: scheme.onErrorContainer, + surface: scheme.surface, + onSurface: scheme.onSurface, + surfaceDim: scheme.surfaceDim, + surfaceBright: scheme.surfaceBright, + surfaceContainerLowest: scheme.surfaceContainerLowest, + surfaceContainerLow: scheme.surfaceContainerLow, + surfaceContainer: scheme.surfaceContainer, + surfaceContainerHigh: scheme.surfaceContainerHigh, + surfaceContainerHighest: scheme.surfaceContainerHighest, + onSurfaceVariant: scheme.onSurfaceVariant, + outline: scheme.outline, + outlineVariant: scheme.outlineVariant, + shadow: scheme.shadow, + scrim: scheme.scrim, + inverseSurface: scheme.inverseSurface, + onInverseSurface: scheme.onInverseSurface, + inversePrimary: scheme.inversePrimary, + surfaceTint: scheme.surfaceTint, + ), + textTheme: legacy.TextTheme( + displayLarge: textTheme.displayLarge, + displayMedium: textTheme.displayMedium, + displaySmall: textTheme.displaySmall, + headlineLarge: textTheme.headlineLarge, + headlineMedium: textTheme.headlineMedium, + headlineSmall: textTheme.headlineSmall, + titleLarge: textTheme.titleLarge, + titleMedium: textTheme.titleMedium, + titleSmall: textTheme.titleSmall, + bodyLarge: textTheme.bodyLarge, + bodyMedium: textTheme.bodyMedium, + bodySmall: textTheme.bodySmall, + labelLarge: textTheme.labelLarge, + labelMedium: textTheme.labelMedium, + labelSmall: textTheme.labelSmall, + ), + ); + } +} diff --git a/packages/material_ui/pending_changelogs/change_2026_07_29_compatibility_bridge.yaml b/packages/material_ui/pending_changelogs/change_2026_07_29_compatibility_bridge.yaml new file mode 100644 index 000000000000..ed05a52525a4 --- /dev/null +++ b/packages/material_ui/pending_changelogs/change_2026_07_29_compatibility_bridge.yaml @@ -0,0 +1,3 @@ +changelog: | + - Adds MaterialUiCompatibilityBridge for legacy flutter/material.dart compatibility. +version: minor diff --git a/packages/material_ui/test/migration_utility_test.dart b/packages/material_ui/test/migration_utility_test.dart new file mode 100644 index 000000000000..855dfc78dbdb --- /dev/null +++ b/packages/material_ui/test/migration_utility_test.dart @@ -0,0 +1,266 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ignore_for_file: deprecated_member_use + +import 'package:flutter/material.dart' as legacy; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart' as modern; + +void main() { + testWidgets('MaterialUiCompatibilityBridge provides mapped ColorScheme properties', ( + WidgetTester tester, + ) async { + legacy.ThemeData? capturedLegacyTheme; + + const modernColorScheme = modern.ColorScheme( + brightness: Brightness.dark, + primary: Color(0xFF112233), + onPrimary: Color(0xFF445566), + primaryContainer: Color(0xFF778899), + onPrimaryContainer: Color(0xFFAABBCC), + secondary: Color(0xFFDDEEFF), + onSecondary: Color(0xFF123456), + secondaryContainer: Color(0xFF654321), + onSecondaryContainer: Color(0xFF001122), + tertiary: Color(0xFF334455), + onTertiary: Color(0xFF667788), + tertiaryContainer: Color(0xFF99AABB), + onTertiaryContainer: Color(0xFFCCDDEE), + error: Color(0xFFFF0000), + onError: Color(0xFFFFFFFF), + errorContainer: Color(0xFF880000), + onErrorContainer: Color(0xFFFF8888), + surface: Color(0xFF101010), + onSurface: Color(0xFFEEEEEE), + surfaceDim: Color(0xFF050505), + surfaceBright: Color(0xFF202020), + surfaceContainerLowest: Color(0xFF000000), + surfaceContainerLow: Color(0xFF0A0A0A), + surfaceContainer: Color(0xFF151515), + surfaceContainerHigh: Color(0xFF1F1F1F), + surfaceContainerHighest: Color(0xFF282828), + onSurfaceVariant: Color(0xFFCCCCCC), + outline: Color(0xFF777777), + outlineVariant: Color(0xFF444444), + shadow: Color(0xFF000001), + scrim: Color(0xFF000002), + inverseSurface: Color(0xFFE0E0E0), + onInverseSurface: Color(0xFF1F1F1F), + inversePrimary: Color(0xFF90CAF9), + surfaceTint: Color(0xFF112233), + ); + + final modernThemeData = modern.ThemeData(colorScheme: modernColorScheme); + + await tester.pumpWidget( + modern.MaterialApp( + theme: modernThemeData, + home: modern.MaterialUiCompatibilityBridge( + child: Builder( + builder: (BuildContext context) { + capturedLegacyTheme = legacy.Theme.of(context); + return const SizedBox.shrink(); + }, + ), + ), + ), + ); + + expect(capturedLegacyTheme, isNotNull); + expect(capturedLegacyTheme!.colorScheme.brightness, modernColorScheme.brightness); + expect(capturedLegacyTheme!.colorScheme.primary, modernColorScheme.primary); + expect(capturedLegacyTheme!.colorScheme.onPrimary, modernColorScheme.onPrimary); + expect(capturedLegacyTheme!.colorScheme.secondary, modernColorScheme.secondary); + expect(capturedLegacyTheme!.colorScheme.tertiary, modernColorScheme.tertiary); + expect(capturedLegacyTheme!.colorScheme.error, modernColorScheme.error); + expect(capturedLegacyTheme!.colorScheme.surface, modernColorScheme.surface); + expect(capturedLegacyTheme!.colorScheme.onSurface, modernColorScheme.onSurface); + expect(capturedLegacyTheme!.colorScheme.outline, modernColorScheme.outline); + expect(capturedLegacyTheme!.colorScheme.shadow, modernColorScheme.shadow); + expect(capturedLegacyTheme!.colorScheme.scrim, modernColorScheme.scrim); + }); + + testWidgets('MaterialUiCompatibilityBridge maps platform and visualDensity properties', ( + WidgetTester tester, + ) async { + legacy.ThemeData? capturedLegacyTheme; + + final modernThemeData = modern.ThemeData( + platform: TargetPlatform.iOS, + visualDensity: modern.VisualDensity.compact, + ); + + await tester.pumpWidget( + modern.MaterialApp( + theme: modernThemeData, + home: modern.MaterialUiCompatibilityBridge( + child: Builder( + builder: (BuildContext context) { + capturedLegacyTheme = legacy.Theme.of(context); + return const SizedBox.shrink(); + }, + ), + ), + ), + ); + + expect(capturedLegacyTheme, isNotNull); + expect(capturedLegacyTheme!.platform, modernThemeData.platform); + expect(capturedLegacyTheme!.visualDensity.horizontal, modernThemeData.visualDensity.horizontal); + expect(capturedLegacyTheme!.visualDensity.vertical, modernThemeData.visualDensity.vertical); + }); + + testWidgets('MaterialUiCompatibilityBridge maps TextTheme properties', ( + WidgetTester tester, + ) async { + legacy.ThemeData? capturedLegacyTheme; + + const modernTextTheme = modern.TextTheme( + displayLarge: TextStyle(fontSize: 57.0, fontWeight: FontWeight.w400), + headlineMedium: TextStyle(fontSize: 28.0, fontWeight: FontWeight.w600), + titleLarge: TextStyle(fontSize: 22.0, fontWeight: FontWeight.w500), + bodyLarge: TextStyle(fontSize: 16.0, color: Color(0xFF123456)), + labelSmall: TextStyle(fontSize: 11.0, letterSpacing: 0.5), + ); + + final modernThemeData = modern.ThemeData(textTheme: modernTextTheme); + + await tester.pumpWidget( + modern.MaterialApp( + theme: modernThemeData, + home: modern.MaterialUiCompatibilityBridge( + child: Builder( + builder: (BuildContext context) { + capturedLegacyTheme = legacy.Theme.of(context); + return const SizedBox.shrink(); + }, + ), + ), + ), + ); + + expect(capturedLegacyTheme, isNotNull); + expect( + capturedLegacyTheme!.textTheme.displayLarge?.fontSize, + modernTextTheme.displayLarge?.fontSize, + ); + expect( + capturedLegacyTheme!.textTheme.headlineMedium?.fontSize, + modernTextTheme.headlineMedium?.fontSize, + ); + expect( + capturedLegacyTheme!.textTheme.headlineMedium?.fontWeight, + modernTextTheme.headlineMedium?.fontWeight, + ); + expect( + capturedLegacyTheme!.textTheme.titleLarge?.fontSize, + modernTextTheme.titleLarge?.fontSize, + ); + expect(capturedLegacyTheme!.textTheme.bodyLarge?.color, modernTextTheme.bodyLarge?.color); + expect( + capturedLegacyTheme!.textTheme.labelSmall?.fontSize, + modernTextTheme.labelSmall?.fontSize, + ); + }); + + testWidgets('MaterialUiCompatibilityBridge provides MaterialLocalizations for custom locales', ( + WidgetTester tester, + ) async { + legacy.MaterialLocalizations? capturedLegacyLocalizations; + + await tester.pumpWidget( + modern.MaterialApp( + locale: const Locale('es', 'ES'), + localizationsDelegates: modern.GlobalMaterialLocalizations.delegates, + supportedLocales: const [Locale('en', 'US'), Locale('es', 'ES')], + home: modern.MaterialUiCompatibilityBridge( + child: Builder( + builder: (BuildContext context) { + capturedLegacyLocalizations = legacy.MaterialLocalizations.of(context); + return const SizedBox.shrink(); + }, + ), + ), + ), + ); + + expect(capturedLegacyLocalizations, isNotNull); + expect(capturedLegacyLocalizations!.okButtonLabel, 'ACEPTAR'); + expect(capturedLegacyLocalizations!.cancelButtonLabel, 'Cancelar'); + }); + + testWidgets('MaterialUiCompatibilityBridge allows rendering legacy Material widgets', ( + WidgetTester tester, + ) async { + await tester.pumpWidget( + modern.MaterialApp( + home: modern.MaterialUiCompatibilityBridge( + child: legacy.Scaffold( + appBar: legacy.AppBar(title: const Text('Legacy Title')), + body: Center( + child: legacy.ElevatedButton(onPressed: () {}, child: const Text('Legacy Button')), + ), + ), + ), + ), + ); + + expect(find.text('Legacy Title'), findsOneWidget); + expect(find.text('Legacy Button'), findsOneWidget); + expect(find.byType(legacy.ElevatedButton), findsOneWidget); + expect(find.byType(legacy.Scaffold), findsOneWidget); + }); + + testWidgets('MaterialUiCompatibilityBridge supports custom delegates', ( + WidgetTester tester, + ) async { + legacy.MaterialLocalizations? capturedLegacyLocalizations; + + await tester.pumpWidget( + modern.MaterialApp( + home: modern.MaterialUiCompatibilityBridge( + delegates: const >[ + legacy.DefaultMaterialLocalizations.delegate, + ], + child: Builder( + builder: (BuildContext context) { + capturedLegacyLocalizations = legacy.MaterialLocalizations.of(context); + return const SizedBox.shrink(); + }, + ), + ), + ), + ); + + expect(capturedLegacyLocalizations, isNotNull); + expect(capturedLegacyLocalizations!.okButtonLabel, isNotEmpty); + }); + + testWidgets('MaterialUiCompatibilityBridge works at root in MaterialApp.builder', ( + WidgetTester tester, + ) async { + await tester.pumpWidget( + modern.MaterialApp( + builder: (BuildContext context, Widget? child) { + return modern.MaterialUiCompatibilityBridge(child: child!); + }, + home: modern.Scaffold( + body: Column( + children: [ + modern.ElevatedButton(onPressed: () {}, child: const Text('Modern Button')), + legacy.ElevatedButton(onPressed: () {}, child: const Text('Legacy Button')), + ], + ), + ), + ), + ); + + expect(find.text('Modern Button'), findsOneWidget); + expect(find.text('Legacy Button'), findsOneWidget); + expect(find.byType(modern.ElevatedButton), findsOneWidget); + expect(find.byType(legacy.ElevatedButton), findsOneWidget); + }); +}