Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
278 changes: 201 additions & 77 deletions lib/src/macos_app.dart
Original file line number Diff line number Diff line change
@@ -1,21 +1,54 @@
import 'package:macos_ui/macos_ui.dart';
import 'package:flutter/material.dart' as m;
import 'package:flutter/cupertino.dart' as c;

/// Defines an application that uses macOS design
/// An application that uses macOS design.
///
/// todo: Documentation
/// A convenience widget that wraps a number of widgets that are commonly
/// required for an macOS-design targeting application. It builds upon a
/// [WidgetsApp] by macOS specific defaulting such as fonts and scrolling
/// physics.
///
/// The [MacosApp] configures the top-level [Navigator] to search for routes
/// in the following order:
///
/// 1. For the `/` route, the [home] property, if non-null, is used.
///
/// 2. Otherwise, the [routes] table is used, if it has an entry for the route.
///
/// 3. Otherwise, [onGenerateRoute] is called, if provided. It should return a
/// non-null value for any _valid_ route not handled by [home] and [routes].
///
/// 4. Finally if all else fails [onUnknownRoute] is called.
///
/// If [home], [routes], [onGenerateRoute], and [onUnknownRoute] are all null,
/// and [builder] is not null, then no [Navigator] is created.
///
/// This widget also configures the observer of the top-level [Navigator] (if
/// any) to perform [Hero] animations.
class MacosApp extends StatefulWidget {
/// Creates a MacosApp.
///
/// At least one of [home], [routes], [onGenerateRoute], or [builder] must be
/// non-null. If only [routes] is given, it must include an entry for the
/// [Navigator.defaultRouteName] (`/`), since that is the route used when the
/// application is launched with an intent that specifies an otherwise
/// unsupported route.
///
/// This class creates an instance of [WidgetsApp].
///
/// The boolean arguments, [routes], and [navigatorObservers], must not be null.
const MacosApp({
Key? key,
this.navigatorKey,
this.home,
Map<String, Widget Function(BuildContext)> this.routes =
const <String, WidgetBuilder>{},
this.initialRoute,
this.onGenerateRoute,
this.onGenerateInitialRoutes,
this.onUnknownRoute,
this.navigatorObservers = const <NavigatorObserver>[],
this.initialRoute,
this.pageRouteBuilder,
this.home,
this.routes = const <String, WidgetBuilder>{},
List<NavigatorObserver> this.navigatorObservers =
const <NavigatorObserver>[],
this.builder,
this.title = '',
this.onGenerateTitle,
Expand All @@ -29,33 +62,30 @@ class MacosApp extends StatefulWidget {
this.checkerboardRasterCacheImages = false,
this.checkerboardOffscreenLayers = false,
this.showSemanticsDebugger = false,
this.debugShowWidgetInspector = false,
this.debugShowCheckedModeBanner = true,
this.inspectorSelectButtonBuilder,
this.shortcuts,
this.actions,
this.restorationScopeId,
this.themeMode,
this.style,
this.darkStyle,
this.themeMode,
}) : routeInformationProvider = null,
routeInformationParser = null,
routerDelegate = null,
backButtonDispatcher = null,
super(key: key);

/// Creates a [MacosApp] that uses the [Router] instead of a [Navigator].
MacosApp.router({
Key? key,
this.style,
this.darkStyle,
this.themeMode,
this.routeInformationProvider,
required this.routeInformationParser,
required this.routerDelegate,
BackButtonDispatcher? backButtonDispatcher,
required RouteInformationParser<Object> this.routeInformationParser,
required RouterDelegate<Object> this.routerDelegate,
this.backButtonDispatcher,
this.builder,
this.title = '',
this.onGenerateTitle,
required Color this.primaryColor,
this.primaryColor,
this.locale,
this.localizationsDelegates,
this.localeListResolutionCallback,
Expand All @@ -65,100 +95,196 @@ class MacosApp extends StatefulWidget {
this.checkerboardRasterCacheImages = false,
this.checkerboardOffscreenLayers = false,
this.showSemanticsDebugger = false,
this.debugShowWidgetInspector = false,
this.debugShowCheckedModeBanner = true,
this.inspectorSelectButtonBuilder,
this.shortcuts,
this.actions,
}) : assert(routeInformationParser != null && routerDelegate != null,
'The routeInformationParser and routerDelegate cannot be null.'),
assert(supportedLocales.isNotEmpty),
this.restorationScopeId,
this.themeMode,
this.style,
this.darkStyle,
}) : assert(supportedLocales.isNotEmpty),
navigatorObservers = null,
backButtonDispatcher =
backButtonDispatcher ?? RootBackButtonDispatcher(),
navigatorKey = null,
onGenerateRoute = null,
pageRouteBuilder = null,
home = null,
onGenerateInitialRoutes = null,
onUnknownRoute = null,
routes = null,
initialRoute = null,
super(key: key);

final Map<Type, Action<Intent>>? actions;

final BackButtonDispatcher? backButtonDispatcher;

final TransitionBuilder? builder;

final bool checkerboardOffscreenLayers;

final bool checkerboardRasterCacheImages;

final Style? darkStyle;

static bool debugAllowBannerOverride = true;

final bool debugShowCheckedModeBanner;

final bool debugShowWidgetInspector;

static bool debugShowWidgetInspectorOverride = false;
/// {@macro flutter.widgets.widgetsApp.navigatorKey}
final GlobalKey<NavigatorState>? navigatorKey;

/// {@macro flutter.widgets.widgetsApp.home}
final Widget? home;

final String? initialRoute;
/// The application's top-level routing table.
///
/// When a named route is pushed with [Navigator.pushNamed], the route name is
/// looked up in this map. If the name is present, the associated
/// [WidgetBuilder] is used to construct a [CupertinoPageRoute] that performs
/// an appropriate transition, including [Hero] animations, to the new route.
///
/// {@macro flutter.widgets.widgetsApp.routes}
final Map<String, WidgetBuilder>? routes;

final InspectorSelectButtonBuilder? inspectorSelectButtonBuilder;
/// {@macro flutter.widgets.widgetsApp.initialRoute}
final String? initialRoute;

final Locale? locale;
/// {@macro flutter.widgets.widgetsApp.onGenerateRoute}
final RouteFactory? onGenerateRoute;

final LocaleListResolutionCallback? localeListResolutionCallback;
/// {@macro flutter.widgets.widgetsApp.onGenerateInitialRoutes}
final InitialRouteListFactory? onGenerateInitialRoutes;

final LocaleResolutionCallback? localeResolutionCallback;
/// {@macro flutter.widgets.widgetsApp.onUnknownRoute}
final RouteFactory? onUnknownRoute;

final Iterable<LocalizationsDelegate<dynamic>>? localizationsDelegates;
/// {@macro flutter.widgets.widgetsApp.navigatorObservers}
final List<NavigatorObserver>? navigatorObservers;

final GlobalKey<NavigatorState>? navigatorKey;
/// {@macro flutter.widgets.widgetsApp.routeInformationProvider}
final RouteInformationProvider? routeInformationProvider;

final List<NavigatorObserver>? navigatorObservers;
/// {@macro flutter.widgets.widgetsApp.routeInformationParser}
final RouteInformationParser<Object>? routeInformationParser;

final InitialRouteListFactory? onGenerateInitialRoutes;
/// {@macro flutter.widgets.widgetsApp.routerDelegate}
final RouterDelegate<Object>? routerDelegate;

final RouteFactory? onGenerateRoute;
/// {@macro flutter.widgets.widgetsApp.backButtonDispatcher}
final BackButtonDispatcher? backButtonDispatcher;

final GenerateAppTitle? onGenerateTitle;
/// {@macro flutter.widgets.widgetsApp.builder}
final TransitionBuilder? builder;

final RouteFactory? onUnknownRoute;
/// {@macro flutter.widgets.widgetsApp.title}
///
/// This value is passed unmodified to [WidgetsApp.title].
final String title;

final PageRouteFactory? pageRouteBuilder;
/// {@macro flutter.widgets.widgetsApp.onGenerateTitle}
///
/// This value is passed unmodified to [WidgetsApp.onGenerateTitle].
final GenerateAppTitle? onGenerateTitle;

/// {@macro flutter.widgets.widgetsApp.color}
final Color? primaryColor;

final RouteInformationParser<Object>? routeInformationParser;
/// {@macro flutter.widgets.widgetsApp.locale}
final Locale? locale;

final RouteInformationProvider? routeInformationProvider;
/// {@macro flutter.widgets.widgetsApp.localizationsDelegates}
final Iterable<LocalizationsDelegate<dynamic>>? localizationsDelegates;

final RouterDelegate<Object>? routerDelegate;
/// {@macro flutter.widgets.widgetsApp.localeListResolutionCallback}
///
/// This callback is passed along to the [WidgetsApp] built by this widget.
final LocaleListResolutionCallback? localeListResolutionCallback;

final Map<String, WidgetBuilder>? routes;
/// {@macro flutter.widgets.LocaleResolutionCallback}
///
/// This callback is passed along to the [WidgetsApp] built by this widget.
final LocaleResolutionCallback? localeResolutionCallback;

final Map<LogicalKeySet, Intent>? shortcuts;
/// {@macro flutter.widgets.widgetsApp.supportedLocales}
///
/// It is passed along unmodified to the [WidgetsApp] built by this widget.
final Iterable<Locale> supportedLocales;

/// Turns on a performance overlay.
///
/// See also:
///
/// * <https://flutter.dev/debugging/#performanceoverlay>
final bool showPerformanceOverlay;

static bool showPerformanceOverlayOverride = false;
/// Turns on checkerboarding of raster cache images.
final bool checkerboardRasterCacheImages;

/// Turns on checkerboarding of layers rendered to offscreen bitmaps.
final bool checkerboardOffscreenLayers;

/// Turns on an overlay that shows the accessibility information
/// reported by the framework.
final bool showSemanticsDebugger;

final Style? style;
/// {@macro flutter.widgets.widgetsApp.debugShowCheckedModeBanner}
final bool debugShowCheckedModeBanner;

final Iterable<Locale> supportedLocales;
/// {@macro flutter.widgets.widgetsApp.shortcuts}
/// {@tool snippet}
/// This example shows how to add a single shortcut for
/// [LogicalKeyboardKey.select] to the default shortcuts without needing to
/// add your own [Shortcuts] widget.
///
/// Alternatively, you could insert a [Shortcuts] widget with just the mapping
/// you want to add between the [WidgetsApp] and its child and get the same
/// effect.
///
/// ```dart
/// Widget build(BuildContext context) {
/// return WidgetsApp(
/// shortcuts: <LogicalKeySet, Intent>{
/// ... WidgetsApp.defaultShortcuts,
/// LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(),
/// },
/// color: const Color(0xFFFF0000),
/// builder: (BuildContext context, Widget? child) {
/// return const Placeholder();
/// },
/// );
/// }
/// ```
/// {@end-tool}
/// {@macro flutter.widgets.widgetsApp.shortcuts.seeAlso}
final Map<LogicalKeySet, Intent>? shortcuts;

/// {@macro flutter.widgets.widgetsApp.actions}
/// {@tool snippet}
/// This example shows how to add a single action handling an
/// [ActivateAction] to the default actions without needing to
/// add your own [Actions] widget.
///
/// Alternatively, you could insert a [Actions] widget with just the mapping
/// you want to add between the [WidgetsApp] and its child and get the same
/// effect.
///
/// ```dart
/// Widget build(BuildContext context) {
/// return WidgetsApp(
/// actions: <Type, Action<Intent>>{
/// ... WidgetsApp.defaultActions,
/// ActivateAction: CallbackAction(
/// onInvoke: (Intent intent) {
/// // Do something here...
/// return null;
/// },
/// ),
/// },
/// color: const Color(0xFFFF0000),
/// builder: (BuildContext context, Widget? child) {
/// return const Placeholder();
/// },
/// );
/// }
/// ```
/// {@end-tool}
/// {@macro flutter.widgets.widgetsApp.actions.seeAlso}
final Map<Type, Action<Intent>>? actions;

/// {@macro flutter.widgets.widgetsApp.restorationScopeId}
final String? restorationScopeId;

/// The current theme mode.
final ThemeMode? themeMode;

final String title;
/// The style used if [themeMode] is [ThemeMode.dark]
final Style? darkStyle;

/// The style used if [themeMode] is [ThemeMode.light]
final Style? style;

@override
_MacosAppState createState() => _MacosAppState();
Expand Down Expand Up @@ -191,23 +317,21 @@ class _MacosAppState extends State<MacosApp> {

Widget _builder(BuildContext context, Widget? child) {
final theme = this.theme(context);
return m.Material(
child: MacosTheme(
style: theme,
child: DefaultTextStyle(
style: TextStyle(
color: theme.typography?.body?.color,
),
child: child!,
return MacosTheme(
style: theme,
child: DefaultTextStyle(
style: TextStyle(
color: theme.typography?.body?.color,
),
child: child!,
),
);
}

Widget _buildApp(BuildContext context) {
final defaultColor = widget.primaryColor ?? CupertinoColors.systemBlue;
if (_usesRouter) {
return m.MaterialApp.router(
return c.CupertinoApp.router(
key: GlobalObjectKey(this),
routeInformationProvider: widget.routeInformationProvider,
routeInformationParser: widget.routeInformationParser!,
Expand All @@ -230,7 +354,7 @@ class _MacosAppState extends State<MacosApp> {
actions: widget.actions,
);
}
return m.MaterialApp(
return c.CupertinoApp(
key: GlobalObjectKey(this),
navigatorKey: widget.navigatorKey,
navigatorObservers: widget.navigatorObservers!,
Expand Down