adding packages

This commit is contained in:
2026-01-15 14:38:46 -08:00
parent ef86e3ab6a
commit 6869cf47e5
5253 changed files with 726695 additions and 34 deletions
@@ -0,0 +1,5 @@
library storyboard;
export 'src/storyboard.dart';
export 'src/nested_app.dart';
export 'package:device_frame/device_frame.dart';
@@ -0,0 +1,21 @@
import 'package:flutter/material.dart';
class CustomRect extends CustomClipper<Rect> {
final Offset offset;
CustomRect(this.offset);
@override
Rect getClip(Size size) {
return Rect.fromLTWH(
offset.dx,
offset.dy,
size.width,
size.height,
);
}
@override
bool shouldReclip(CustomRect oldClipper) {
return true;
}
}
@@ -0,0 +1,44 @@
import 'package:flutter/widgets.dart';
class MediaQueryObserver extends StatefulWidget {
final Widget child;
final MediaQueryData? data;
const MediaQueryObserver({
required this.child,
required this.data,
});
@override
_MediaQueryObserverState createState() => _MediaQueryObserverState();
}
class _MediaQueryObserverState extends State<MediaQueryObserver>
with WidgetsBindingObserver {
@override
void didChangeMetrics() {
setState(() {});
super.didChangeMetrics();
}
@override
void initState() {
WidgetsBinding.instance?.addObserver(this);
super.initState();
}
@override
void dispose() {
WidgetsBinding.instance?.removeObserver(this);
super.dispose();
}
@override
Widget build(BuildContext context) {
return MediaQuery(
data: widget.data ??
MediaQueryData.fromWindow(WidgetsBinding.instance!.window),
child: widget.child,
);
}
}
@@ -0,0 +1,283 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'media_query_observer.dart';
class NestedApp extends StatefulWidget {
const NestedApp({
Key? key,
required this.route,
required this.size,
required this.customWidget,
required this.materialApp,
required this.cupertinoApp,
required this.widgetsApp,
required this.child,
}) : super(key: key);
final Widget? child;
final bool customWidget;
final CupertinoApp? cupertinoApp;
final MaterialApp? materialApp;
final WidgetsApp? widgetsApp;
final RouteSettings? route;
final Size? size;
@override
_NestedAppState createState() => _NestedAppState();
}
class _NestedAppState extends State<NestedApp> {
late GlobalObjectKey<NavigatorState> _navKey;
@override
void initState() {
setup();
super.initState();
}
void setup() {
_navKey = GlobalObjectKey<NavigatorState>(this);
WidgetsBinding.instance?.addPostFrameCallback((_) {
if (widget.route != null) {
_navKey.currentState?.pushReplacementNamed(
widget.route!.name!,
arguments: widget.route!.arguments,
);
}
});
}
@override
void didUpdateWidget(NestedApp oldWidget) {
if (oldWidget.materialApp != widget.materialApp) {
if (mounted) setState(setup);
}
if (oldWidget.cupertinoApp != widget.cupertinoApp) {
if (mounted) setState(setup);
}
if (oldWidget.widgetsApp != widget.widgetsApp) {
if (mounted) setState(setup);
}
// if (oldWidget?.route != widget?.route) {
// if (mounted) setState(() => setup());
// }
super.didUpdateWidget(oldWidget);
}
Map<String, WidgetBuilder> get routes {
if (widget.materialApp != null) return widget.materialApp!.routes!;
if (widget.widgetsApp != null) return widget.widgetsApp!.routes!;
if (widget.cupertinoApp != null) return widget.cupertinoApp!.routes!;
return const <String, WidgetBuilder>{};
}
Locale? get locale {
if (widget.materialApp != null) return widget.materialApp?.locale;
if (widget.widgetsApp != null) return widget.widgetsApp?.locale;
if (widget.cupertinoApp != null) return widget.cupertinoApp?.locale;
return null;
}
Iterable<LocalizationsDelegate<dynamic>>? get localizationsDelegates {
if (widget.materialApp != null) {
return widget.materialApp?.localizationsDelegates;
}
if (widget.widgetsApp != null) {
return widget.widgetsApp?.localizationsDelegates;
}
if (widget.cupertinoApp != null) {
return widget.cupertinoApp?.localizationsDelegates;
}
return null;
}
Iterable<Locale> get supportedLocales {
if (widget.materialApp != null) return widget.materialApp!.supportedLocales;
if (widget.widgetsApp != null) return widget.widgetsApp!.supportedLocales;
if (widget.cupertinoApp != null) {
return widget.cupertinoApp!.supportedLocales;
}
return const <Locale>[Locale('en', 'US')];
}
LocaleResolutionCallback? get localeResolutionCallback {
if (widget.materialApp != null) {
return widget.materialApp?.localeResolutionCallback;
}
if (widget.widgetsApp != null) {
return widget.widgetsApp?.localeResolutionCallback;
}
if (widget.cupertinoApp != null) {
return widget.cupertinoApp?.localeResolutionCallback;
}
return null;
}
LocaleListResolutionCallback? get localeListResolutionCallback {
if (widget.materialApp != null) {
return widget.materialApp?.localeListResolutionCallback;
}
if (widget.widgetsApp != null) {
return widget.widgetsApp?.localeListResolutionCallback;
}
if (widget.cupertinoApp != null) {
return widget.cupertinoApp?.localeListResolutionCallback;
}
return null;
}
Map<Type, Action<Intent>>? get actions {
if (widget.materialApp != null) return widget.materialApp?.actions;
if (widget.widgetsApp != null) return widget.widgetsApp?.actions;
if (widget.cupertinoApp != null) return widget.cupertinoApp?.actions;
return null;
}
Map<LogicalKeySet, Intent>? get shortcuts {
if (widget.materialApp != null) return widget.materialApp?.shortcuts;
if (widget.widgetsApp != null) return widget.widgetsApp?.shortcuts;
if (widget.cupertinoApp != null) return widget.cupertinoApp?.shortcuts;
return null;
}
String get title {
if (widget.materialApp != null) return widget.materialApp!.title;
if (widget.widgetsApp != null) return widget.widgetsApp!.title;
if (widget.cupertinoApp != null) return widget.cupertinoApp!.title;
return '';
}
String Function(BuildContext)? get onGenerateTitle {
if (widget.materialApp != null) return widget.materialApp?.onGenerateTitle;
if (widget.widgetsApp != null) return widget.widgetsApp?.onGenerateTitle;
if (widget.cupertinoApp != null) {
return widget.cupertinoApp?.onGenerateTitle;
}
return null;
}
Widget builder(BuildContext context, Widget child) {
if (widget.materialApp?.builder != null) {
return widget.materialApp!.builder!(context, child);
}
if (widget.widgetsApp?.builder != null) {
return widget.widgetsApp!.builder!(context, child);
}
if (widget.cupertinoApp?.builder != null) {
return widget.cupertinoApp!.builder!(context, child);
}
return child;
}
@override
Widget build(BuildContext context) {
if (widget.customWidget) {
return MaterialApp(
theme: widget.materialApp?.theme ?? ThemeData.light(),
darkTheme: widget.materialApp?.darkTheme ?? ThemeData.dark(),
themeMode: widget.materialApp?.themeMode ?? ThemeMode.system,
debugShowCheckedModeBanner: false,
home: widget.child,
builder: (context, val) => MediaQueryObserver(
data: MediaQuery.of(context).copyWith(size: widget.size),
child: builder(context, val ?? Container()),
),
color: widget.materialApp?.color,
locale: locale,
localizationsDelegates: localizationsDelegates,
supportedLocales: supportedLocales,
localeResolutionCallback: localeResolutionCallback,
localeListResolutionCallback: localeListResolutionCallback,
actions: actions,
shortcuts: shortcuts,
title: title,
onGenerateTitle: onGenerateTitle,
);
}
if (widget.materialApp != null) {
return MaterialApp(
navigatorKey: _navKey,
theme: widget.materialApp?.theme ?? ThemeData.light(),
darkTheme: widget.materialApp?.darkTheme ?? ThemeData.dark(),
themeMode: widget.materialApp?.themeMode ?? ThemeMode.system,
debugShowCheckedModeBanner: false,
routes: routes,
onGenerateRoute: widget.materialApp?.onGenerateRoute,
onUnknownRoute: widget.materialApp?.onUnknownRoute,
onGenerateInitialRoutes: widget.materialApp?.onGenerateInitialRoutes,
home: widget.child,
builder: (context, val) => MediaQueryObserver(
data: MediaQuery.of(context).copyWith(size: widget.size),
child: builder(context, val ?? Container()),
),
// initialRoute: route?.name,
color: widget.materialApp?.color,
locale: locale,
localizationsDelegates: localizationsDelegates,
supportedLocales: supportedLocales,
localeResolutionCallback: localeResolutionCallback,
localeListResolutionCallback: localeListResolutionCallback,
actions: actions,
shortcuts: shortcuts,
title: title,
onGenerateTitle: onGenerateTitle,
);
}
if (widget.cupertinoApp != null) {
return CupertinoApp(
navigatorKey: _navKey,
theme: widget.cupertinoApp?.theme ?? const CupertinoThemeData(),
debugShowCheckedModeBanner: false,
routes: routes,
onGenerateRoute: widget.cupertinoApp?.onGenerateRoute,
onUnknownRoute: widget.cupertinoApp?.onUnknownRoute,
onGenerateInitialRoutes: widget.cupertinoApp?.onGenerateInitialRoutes,
home: widget.child,
builder: (context, val) => MediaQueryObserver(
data: MediaQuery.of(context).copyWith(size: widget.size),
child: builder(context, val ?? Container()),
),
// initialRoute: route?.name,
color: widget.cupertinoApp?.color,
locale: locale,
localizationsDelegates: localizationsDelegates,
supportedLocales: supportedLocales,
localeResolutionCallback: localeResolutionCallback,
localeListResolutionCallback: localeListResolutionCallback,
actions: actions,
shortcuts: shortcuts,
title: title,
onGenerateTitle: onGenerateTitle,
);
}
if (widget.widgetsApp != null) {
return WidgetsApp(
navigatorKey: _navKey,
debugShowCheckedModeBanner: false,
routes: routes,
onGenerateRoute: widget.widgetsApp?.onGenerateRoute,
onUnknownRoute: widget.widgetsApp?.onUnknownRoute,
onGenerateInitialRoutes: widget.widgetsApp?.onGenerateInitialRoutes,
home: widget.child,
builder: (context, val) => MediaQueryObserver(
data: MediaQuery.of(context).copyWith(size: widget.size),
child: builder(context, val ?? Container()),
),
// initialRoute: route?.name,
color: widget.widgetsApp?.color ?? Colors.transparent,
locale: locale,
localizationsDelegates: localizationsDelegates,
supportedLocales: supportedLocales,
localeResolutionCallback: localeResolutionCallback,
localeListResolutionCallback: localeListResolutionCallback,
actions: actions,
shortcuts: shortcuts,
title: title,
onGenerateTitle: onGenerateTitle,
);
}
return widget.child ?? Container();
}
}
@@ -0,0 +1,192 @@
part of storyboard;
class CustomLane {
final String title;
final List<CustomScreen> children;
CustomLane({
required this.title,
required this.children,
});
}
class CustomScreen {
final String? label;
final Size? size;
final Widget child;
final DeviceInfo? cupertinoDevice;
final DeviceInfo? androidDevice;
final Orientation? orientation;
CustomScreen({
required this.size,
required this.child,
this.label,
this.androidDevice,
this.cupertinoDevice,
this.orientation = Orientation.portrait,
});
CustomScreen copyWith({
String? label,
Size? size,
Widget? child,
DeviceInfo? cupertinoDevice,
DeviceInfo? androidDevice,
Orientation? orientation,
}) {
return CustomScreen(
label: label ?? this.label,
size: size ?? this.size,
child: child ?? this.child,
cupertinoDevice: cupertinoDevice ?? this.cupertinoDevice,
androidDevice: androidDevice ?? this.androidDevice,
orientation: orientation ?? this.orientation,
);
}
}
typedef LaneBuilder = Widget Function(
BuildContext context,
String? title,
Widget child,
);
class _Lane extends StatelessWidget {
const _Lane({
Key? key,
required this.children,
required this.crossAxisCount,
required this.scale,
required this.androidDevice,
required this.cupertinoDevice,
this.title,
this.itemBuilder,
this.laneBuilder,
this.size,
this.shadow,
this.orientation = Orientation.portrait,
}) : super(key: key);
final List<CustomScreen> children;
final int? crossAxisCount;
final double scale;
final DeviceInfo? cupertinoDevice;
final DeviceInfo? androidDevice;
final Orientation? orientation;
final Widget Function(BuildContext, Widget)? itemBuilder;
final LaneBuilder? laneBuilder;
final String? title;
final Size? size;
final BoxShadow? shadow;
@override
Widget build(BuildContext context) {
List<CustomScreen> _children = [];
for (final child in children) {
if (itemBuilder != null) {
_children
.add(child.copyWith(child: itemBuilder!(context, child.child)));
} else {
_children.add(child);
}
}
int _itemsPerRow;
int _rows;
if (crossAxisCount != null) {
_rows = (_children.length / crossAxisCount!).ceil();
_itemsPerRow = crossAxisCount!;
} else {
_rows = 1;
_itemsPerRow = _children.length;
}
Widget _child;
_child = buildWrapLane(_rows, _children, _itemsPerRow, context);
if (laneBuilder != null) {
return laneBuilder!(context, title, _child);
}
return _child;
}
Widget buildWrapLane(int _rows, List<CustomScreen> _children,
int _itemsPerRow, BuildContext context) {
return Container(
margin: const EdgeInsets.all(_kSpacing / 2),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (var i = 0; i < _rows; i++)
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: _children
.skip(i * _itemsPerRow)
.take(_itemsPerRow)
.toList()
.map((e) {
return Column(
children: [
Container(
padding: const EdgeInsets.all(_kSpacing / 2),
child: _buildChild(context, e),
),
if (e.label != null)
SizedBox(
height: _kLabelHeight,
child: Center(child: RoundedLabel(e.label!)),
),
],
);
}).toList(),
),
],
),
);
}
Widget _buildChild(BuildContext context, CustomScreen e) {
return Container(
decoration: shadow == null ? null : BoxDecoration(boxShadow: [shadow!]),
child: MediaQueryObserver(
data: MediaQuery.of(context).copyWith(size: size),
child: Builder(
builder: (context) {
Widget _child = e.child;
if (itemBuilder != null) {
_child = itemBuilder!(context, _child);
}
DeviceInfo? _ios = e.cupertinoDevice ?? cupertinoDevice;
DeviceInfo? _android = e.androidDevice ?? androidDevice;
Orientation _orientation =
e.orientation ?? orientation ?? Orientation.portrait;
if (_ios != null) {
return DeviceFrame(
orientation: _orientation,
device: _ios,
screen: VirtualKeyboard(
isEnabled: true,
child: _child,
),
);
}
if (_android != null) {
return DeviceFrame(
orientation: _orientation,
device: _android,
screen: VirtualKeyboard(
isEnabled: true,
child: _child,
),
);
}
return SizedBox.fromSize(
size: e.size,
child: _child,
);
},
),
),
);
}
}
@@ -0,0 +1,22 @@
import 'package:flutter/material.dart';
class RoundedLabel extends StatelessWidget {
final String? label;
const RoundedLabel(this.label);
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsetsDirectional.only(top: 10),
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(20),
),
child: Text(
label ?? '',
style: const TextStyle(color: Colors.white, fontSize: 20),
),
);
}
}
@@ -0,0 +1,650 @@
library storyboard;
import 'package:device_frame/device_frame.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'custom_rect_clipper.dart';
import 'media_query_observer.dart';
import 'nested_app.dart';
import 'rounded_label.dart';
part 'render_lane.dart';
const _kSpacing = 40.0;
const _kLabelHeight = 65.0;
const _kCompactBreakpoint = 400.0;
const double _MAXSIZE = 100000;
const double _MINSCALE = 0.0001;
const double _STARTSCALE = 0.35;
const _kTitle = 'Storyboard';
typedef ScreenBuilder = Widget Function(
BuildContext context, RouteSettings route, Widget child);
class StoryBoard extends StatefulWidget {
const StoryBoard({
Key? key,
this.children,
this.customLanes,
this.sizedChildren,
bool showAppBar = false,
Size? childSize,
this.initialOffset = Offset.zero,
this.initialScale = _STARTSCALE,
this.customAppBar,
this.crossAxisCount,
this.title = _kTitle,
this.laneBuilder,
this.childrenLabel = 'Children',
this.sizedChildrenLabel = 'Sized Children',
this.orientation,
this.androidDevice,
this.cupertinoDevice,
this.deviceFrameStyle,
}) : _materialApp = null,
_widgetsApp = null,
_cupertinoApp = null,
_widgets = true,
screenSize = childSize,
customRoutes = null,
hideAppBar = !showAppBar,
enabled = true,
assert(
children != null || customLanes != null || sizedChildren != null),
super(key: key);
const StoryBoard.app({
Key? key,
MaterialApp? materialApp,
WidgetsApp? widgetsApp,
CupertinoApp? cupertinoApp,
List<Widget>? customScreens,
this.enabled = true,
this.screenSize = const Size(400, 700),
this.initialOffset = Offset.zero,
this.initialScale = _STARTSCALE,
this.customLanes,
this.customAppBar,
this.customRoutes,
this.crossAxisCount,
this.title = _kTitle,
this.laneBuilder,
this.childrenLabel = 'Children',
this.sizedChildrenLabel = 'Sized Children',
this.sizedChildren,
this.orientation,
this.androidDevice,
this.cupertinoDevice,
this.deviceFrameStyle,
}) : _materialApp = materialApp,
_widgetsApp = widgetsApp,
_cupertinoApp = cupertinoApp,
children = customScreens,
_widgets = false,
hideAppBar = false,
assert(
materialApp != null || widgetsApp != null || cupertinoApp != null),
super(key: key);
const StoryBoard.cupertino({
Key? key,
required CupertinoApp child,
List<Widget>? customScreens,
this.enabled = true,
this.screenSize = const Size(400, 700),
this.initialOffset = Offset.zero,
this.initialScale = _STARTSCALE,
this.customLanes,
this.customAppBar,
this.customRoutes,
this.crossAxisCount,
this.title = _kTitle,
this.laneBuilder,
this.sizedChildren,
this.childrenLabel = 'Children',
this.sizedChildrenLabel = 'Sized Children',
this.orientation,
this.androidDevice,
this.cupertinoDevice,
this.deviceFrameStyle,
}) : _materialApp = null,
_widgetsApp = null,
_cupertinoApp = child,
children = customScreens,
_widgets = false,
hideAppBar = false,
super(key: key);
const StoryBoard.widgets({
Key? key,
required WidgetsApp child,
List<Widget>? customScreens,
this.enabled = true,
this.screenSize = const Size(400, 700),
this.initialOffset = Offset.zero,
this.initialScale = _STARTSCALE,
this.customLanes,
this.customAppBar,
this.customRoutes,
this.crossAxisCount,
this.title = _kTitle,
this.laneBuilder,
this.sizedChildren,
this.childrenLabel = 'Children',
this.sizedChildrenLabel = 'Sized Children',
this.orientation,
this.androidDevice,
this.cupertinoDevice,
this.deviceFrameStyle,
}) : _materialApp = null,
_widgetsApp = child,
_cupertinoApp = null,
children = customScreens,
_widgets = false,
hideAppBar = false,
super(key: key);
const StoryBoard.material({
Key? key,
required MaterialApp child,
List<Widget>? customScreens,
this.enabled = true,
this.screenSize = const Size(400, 700),
this.initialOffset = Offset.zero,
this.initialScale = _STARTSCALE,
this.customLanes,
this.customAppBar,
this.customRoutes,
this.crossAxisCount,
this.title = _kTitle,
this.laneBuilder,
this.sizedChildren,
this.childrenLabel = 'Children',
this.sizedChildrenLabel = 'Sized Children',
this.orientation,
this.androidDevice,
this.cupertinoDevice,
this.deviceFrameStyle,
}) : _materialApp = child,
_widgetsApp = null,
_cupertinoApp = null,
children = customScreens,
_widgets = false,
hideAppBar = false,
super(key: key);
/// Override the default app bar of the storyboard
final PreferredSizeWidget? customAppBar;
/// Custom routes you want to show with test data
final List<RouteSettings>? customRoutes;
/// Custom screens you want to show
final List<Widget>? children;
/// Children that can have custom sizes
final List<CustomScreen>? sizedChildren;
/// List of custom lanes
final List<CustomLane>? customLanes;
/// You can disable this widget at any time and just return the child
final bool enabled;
/// Initial Offset of the canvas
final Offset initialOffset;
/// Initial Scale of the canvas
final double initialScale;
/// Size for each screen
final Size? screenSize;
/// Wrap your Cupertino App with this widget
final CupertinoApp? _cupertinoApp;
/// Wrap your Material App with this widget
final MaterialApp? _materialApp;
/// Wrap your Widgets App with this widget
final WidgetsApp? _widgetsApp;
/// Number of widgets to show in a row instead of maxLaneWidth
final int? crossAxisCount;
/// Optionally hide the AppBar if used as a widget
final bool hideAppBar;
/// Title for AppBar
final String title;
/// Optionally wrap the lane building process to customize the look
final LaneBuilder? laneBuilder;
/// Label for Custom Screens or Children
final String? childrenLabel, sizedChildrenLabel;
/// Use a cupertino device for every screen and ignore the size
final DeviceInfo? cupertinoDevice;
/// Use a android device for every screen and ignore the size
final DeviceInfo? androidDevice;
/// Device orientation for every screen and ignore the size
final Orientation? orientation;
/// Device Frame style
final DeviceFrameStyle? deviceFrameStyle;
final bool _widgets;
@override
StoryboardController createState() => StoryboardController();
}
class StoryboardController extends State<StoryBoard> {
final TransformationController _controller = TransformationController();
late FocusNode _focusNode;
Offset _offset = Offset.zero;
double _scale = 1;
UniqueKey _key = UniqueKey();
@override
void didUpdateWidget(StoryBoard oldWidget) {
if (oldWidget._widgetsApp != widget._widgetsApp) {
if (mounted) setState(() {});
}
if (oldWidget._materialApp != widget._materialApp) {
if (mounted) setState(() {});
}
if (oldWidget._cupertinoApp != widget._cupertinoApp) {
if (mounted) setState(() {});
}
if (oldWidget.cupertinoDevice != widget.cupertinoDevice) {
if (mounted) setState(() {});
}
if (oldWidget.androidDevice != widget.androidDevice) {
if (mounted) setState(() {});
}
if (oldWidget.orientation != widget.orientation) {
if (mounted) setState(() {});
}
super.didUpdateWidget(oldWidget);
}
@override
void dispose() {
_focusNode.dispose();
super.dispose();
}
@override
void initState() {
_focusNode = FocusNode();
_controller.value.scale(widget.initialScale);
final _offset = widget.initialOffset;
_controller.value.translate(_offset.dx, _offset.dy);
super.initState();
}
void restart() {
_key = UniqueKey();
if (mounted) setState(() {});
}
Widget get app {
if (materialApp != null) return materialApp!;
if (widgetsApp != null) return widgetsApp!;
if (cupertinoApp != null) return cupertinoApp!;
return Container();
}
MaterialApp? get materialApp => widget._materialApp;
WidgetsApp? get widgetsApp => widget._widgetsApp;
CupertinoApp? get cupertinoApp => widget._cupertinoApp;
Widget? get home {
if (materialApp != null) return materialApp?.home;
if (widgetsApp != null) return widgetsApp?.home;
if (cupertinoApp != null) return cupertinoApp?.home;
return null;
}
Map<String, WidgetBuilder>? get routes {
if (materialApp != null) return materialApp?.routes;
if (widgetsApp != null) return widgetsApp?.routes;
if (cupertinoApp != null) return cupertinoApp?.routes;
return null;
}
String? get initialRoute {
if (materialApp != null) return materialApp?.initialRoute;
if (widgetsApp != null) return widgetsApp?.initialRoute;
if (cupertinoApp != null) return cupertinoApp?.initialRoute;
return null;
}
Size? get size {
if (widget.screenSize != null) {
return Size(
widget.screenSize!.width,
widget.screenSize!.height + _kLabelHeight,
);
}
return null;
}
CustomScreen _addChild(
RouteSettings? route, {
Widget? child,
String? label,
bool customWidget = false,
Size? customSize,
}) {
final Size? _size = customSize ?? widget.screenSize ?? size;
return CustomScreen(
size: size,
child: _buildApp(_size, route, child, customWidget),
);
}
Widget _buildApp(
Size? _size,
RouteSettings? route,
Widget? child,
bool customWidget,
) {
return Material(
elevation: 2,
child: SizedBox.fromSize(
size: _size,
child: ClipRect(
clipper: CustomRect(const Offset(0, 0)),
child: NestedApp(
route: route,
size: _size,
customWidget: customWidget,
materialApp: materialApp,
cupertinoApp: cupertinoApp,
widgetsApp: widgetsApp,
child: child,
),
),
),
);
}
@override
Widget build(BuildContext context) {
if (!widget.enabled) return app;
// ignore: unnecessary_nullable_for_final_variable_declarations
final ThemeData? _theme = Theme.of(context);
return MaterialApp(
key: _key,
debugShowCheckedModeBanner: false,
themeMode: materialApp?.themeMode ?? ThemeMode.system,
theme: materialApp?.theme ?? _theme ?? ThemeData.light(),
darkTheme: materialApp?.darkTheme ?? _theme ?? ThemeData.dark(),
// builder: (context, child) => MediaQueryObserver(
// data: MediaQuery.of(context).copyWith(size: size),
// child: child,
// ),
home: Builder(
builder: (context) => DeviceFrameTheme(
// style: Theme.of(context).brightness == Brightness.dark
// ? DeviceFrameStyle.dark()
// : DeviceFrameStyle.light(),
style: widget.deviceFrameStyle ?? DeviceFrameStyle.dark(),
child: LayoutBuilder(
builder: (context, dimens) => Scaffold(
appBar: _buildAppBar(dimens.maxWidth <= _kCompactBreakpoint),
body: InteractiveViewer(
transformationController: _controller,
constrained: false,
minScale: _MINSCALE,
child: SizedBox(
width: _MAXSIZE,
height: _MAXSIZE,
child: renderView(),
),
),
),
),
),
),
);
}
Widget renderView() {
return Stack(
fit: StackFit.expand,
// overflow: Overflow.visible,
clipBehavior: Clip.none,
children: [
if (widget._widgets)
Positioned(
top: _offset.dy,
left: _offset.dx,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (widget.children != null)
_Lane(
cupertinoDevice: widget.cupertinoDevice,
androidDevice: widget.androidDevice,
orientation: widget.orientation,
laneBuilder: widget.laneBuilder,
title: widget.childrenLabel ?? '',
scale: _scale,
size: size,
children: widget.children!
.map(
(e) => CustomScreen(
size: size,
child: e,
),
)
.toList(),
crossAxisCount: widget.crossAxisCount,
shadow: const BoxShadow(
color: Colors.black45,
offset: Offset(2, 2),
blurRadius: 10,
spreadRadius: 2,
),
),
if (widget.sizedChildren != null)
_Lane(
cupertinoDevice: widget.cupertinoDevice,
androidDevice: widget.androidDevice,
orientation: widget.orientation,
laneBuilder: widget.laneBuilder,
title: widget.sizedChildrenLabel ?? '',
scale: _scale,
size: size,
children: widget.sizedChildren!,
crossAxisCount: widget.crossAxisCount,
shadow: const BoxShadow(
color: Colors.black45,
offset: Offset(2, 2),
blurRadius: 10,
spreadRadius: 2,
),
),
if (widget.customLanes != null)
for (final lane in widget.customLanes!)
_Lane(
cupertinoDevice: widget.cupertinoDevice,
androidDevice: widget.androidDevice,
orientation: widget.orientation,
laneBuilder: widget.laneBuilder,
title: lane.title,
scale: _scale,
size: size,
crossAxisCount: widget.crossAxisCount,
children: lane.children,
shadow: const BoxShadow(
color: Colors.black45,
offset: Offset(2, 2),
blurRadius: 10,
spreadRadius: 2,
),
),
],
),
),
if (!widget._widgets) ...[
Positioned(
top: _offset.dy,
left: _offset.dx,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_Lane(
cupertinoDevice: widget.cupertinoDevice,
androidDevice: widget.androidDevice,
orientation: widget.orientation,
scale: _scale,
title: 'Main',
laneBuilder: widget.laneBuilder,
size: size,
crossAxisCount: widget.crossAxisCount,
children: [
if (initialRoute != null)
_addChild(
RouteSettings(name: initialRoute),
label: 'Initial Route',
),
if (home != null)
_addChild(
null,
label: 'Home',
child: home,
),
],
),
if (routes != null)
_Lane(
cupertinoDevice: widget.cupertinoDevice,
androidDevice: widget.androidDevice,
orientation: widget.orientation,
title: 'Routes',
scale: _scale,
laneBuilder: widget.laneBuilder,
size: size,
crossAxisCount: widget.crossAxisCount,
children: [
for (var i = 0; i < routes!.keys.length; i++)
_addChild(
RouteSettings(name: routes!.keys.toList()[i]),
label: routes!.keys.toList()[i],
),
],
),
if (widget.children != null)
_Lane(
cupertinoDevice: widget.cupertinoDevice,
androidDevice: widget.androidDevice,
orientation: widget.orientation,
laneBuilder: widget.laneBuilder,
title: widget.childrenLabel ?? '',
scale: _scale,
size: size,
children: widget.children!
.map((e) => _addChild(
null,
label: null,
child: e,
customWidget: true,
))
.toList(),
crossAxisCount: widget.crossAxisCount,
),
if (widget.customRoutes != null)
_Lane(
cupertinoDevice: widget.cupertinoDevice,
androidDevice: widget.androidDevice,
orientation: widget.orientation,
laneBuilder: widget.laneBuilder,
title: 'Custom Routes',
scale: _scale,
size: size,
crossAxisCount: widget.crossAxisCount,
children: [
for (var i = 0; i < widget.customRoutes!.length; i++)
_addChild(
widget.customRoutes![i],
label: widget.customRoutes![i].name,
),
],
),
if (widget.customLanes != null)
for (final lane in widget.customLanes!)
_Lane(
cupertinoDevice: widget.cupertinoDevice,
androidDevice: widget.androidDevice,
orientation: widget.orientation,
laneBuilder: widget.laneBuilder,
title: lane.title,
scale: _scale,
size: size,
crossAxisCount: widget.crossAxisCount,
children: lane.children
.map((e) => _addChild(
null,
label: e.label,
child: e.child,
customSize: e.size,
customWidget: true,
))
.toList(),
),
],
),
),
],
],
);
}
PreferredSizeWidget? _buildAppBar([bool compact = false]) {
const kRestartKey = 0;
const kResetKey = 1;
const kZoomInKey = 2;
const kZoomOutKey = 4;
if (widget.hideAppBar) {
return null;
} else {
return widget.customAppBar ??
AppBar(
title: const Text(_kTitle),
actions: compact
? [
PopupMenuButton<int>(
icon: const Icon(Icons.more_vert),
itemBuilder: (context) => [
const PopupMenuItem(
value: kRestartKey,
child: Text('Restart App'),
),
],
onSelected: (val) {
if (val == kRestartKey) {
restart();
}
},
),
]
: [
IconButton(
tooltip: 'Restart App',
icon: const Icon(Icons.refresh),
onPressed: restart,
),
],
);
}
}
}