Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
leoafarias committed Aug 8, 2024
1 parent 839b68b commit 9e19286
Showing 24 changed files with 483 additions and 360 deletions.
8 changes: 4 additions & 4 deletions packages/superdeck/build.yaml
Original file line number Diff line number Diff line change
@@ -6,16 +6,16 @@ targets:
- lib/**/*.dart
mix_generator|spec:
generate_for:
- lib/**/*_spec.dart
- lib/**/*.dart
mix_generator|dto:
generate_for:
- lib/**/*_spec.dart
- lib/**/*.dart
mix_generator|enum_utility:
generate_for:
- lib/**/*_spec.dart
- lib/**/*.dart
mix_generator|class_utility:
generate_for:
- lib/**/*_spec.dart
- lib/**/*.dart

global_options:
dart_mappable_builder:
10 changes: 8 additions & 2 deletions packages/superdeck/example/ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -33,6 +33,8 @@ PODS:
- file_picker (0.0.1):
- DKImagePickerController/PhotoGallery
- Flutter
- file_saver (0.0.1):
- Flutter
- Flutter (1.0.0)
- path_provider_foundation (0.0.1):
- Flutter
@@ -49,6 +51,7 @@ PODS:

DEPENDENCIES:
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- file_saver (from `.symlinks/plugins/file_saver/ios`)
- Flutter (from `Flutter`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
@@ -64,6 +67,8 @@ SPEC REPOS:
EXTERNAL SOURCES:
file_picker:
:path: ".symlinks/plugins/file_picker/ios"
file_saver:
:path: ".symlinks/plugins/file_saver/ios"
Flutter:
:path: Flutter
path_provider_foundation:
@@ -77,12 +82,13 @@ SPEC CHECKSUMS:
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
SDWebImage: 981fd7e860af070920f249fd092420006014c3eb
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
url_launcher_ios: 6116280ddcfe98ab8820085d8d76ae7449447586
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe

PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796

2 changes: 1 addition & 1 deletion packages/superdeck/example/ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Flutter
import UIKit

@UIApplicationMain
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
2 changes: 0 additions & 2 deletions packages/superdeck/example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:superdeck/superdeck.dart';

import 'src/style.dart';
import 'src/widget/mix_demo.dart';

void main() async {
debugRepaintRainbowEnabled = true;
await SuperDeckApp.initialize();
runApp(
Builder(builder: (context) {
1 change: 0 additions & 1 deletion packages/superdeck/example/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -11,7 +11,6 @@ environment:
dependencies:
flutter:
sdk: flutter

google_fonts: ^6.2.0
superdeck:
path: ../
2 changes: 1 addition & 1 deletion packages/superdeck/example/pubspec_overrides.yaml
Original file line number Diff line number Diff line change
@@ -5,6 +5,6 @@ dependency_overrides:
# mix:
# path: ../../mix/packages/mix
superdeck:
path: ..
path: ../
superdeck_cli:
path: ../../superdeck_cli
135 changes: 49 additions & 86 deletions packages/superdeck/lib/components/atoms/slide_thumbnail.dart
Original file line number Diff line number Diff line change
@@ -3,40 +3,36 @@ import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:remix/remix.dart';

import '../../helpers/constants.dart';
import '../../helpers/extensions.dart';
import '../../services/reference_service.dart';
import '../../services/snapshot_service.dart';
import '../../superdeck.dart';
import '../molecules/scaled_app.dart';
import 'cache_image_widget.dart';
import 'loading_indicator.dart';
import 'slide_view.dart';

class SlideThumbnail extends HookWidget {
final VoidCallback onTap;
final int index;
final bool selected;
final Slide slide;
final int page;

const SlideThumbnail({
super.key,
required this.index,
required this.selected,
required this.onTap,
required this.slide,
required this.page,
});

@override
Widget build(BuildContext context) {
final navigation = useNavigation();

final processThumbnail = useFuture(
useMemoized(() => _generateThumbnail(slide), [slide]),
);
return LayoutBuilder(builder: (context, constraints) {
final selectedColor =
index == navigation.page ? Colors.blue : Colors.transparent;

final child = LoadingOverlay(
isLoading: processThumbnail.isLoading,
child: processThumbnail.when(
@@ -62,44 +58,42 @@ class SlideThumbnail extends HookWidget {
return GestureDetector(
onTap: onTap,
child: _PreviewContainer(
selectedColor: selectedColor,
child: AbsorbPointer(
child: AspectRatio(
aspectRatio: kAspectRatio,
child: Stack(
children: [
child,
Positioned(
top: 0,
right: 0,
left: 0,
child: SizedBox(
child: processThumbnail.isRefreshing
? const LinearProgressIndicator(
minHeight: 3,
backgroundColor: Colors.transparent,
)
: null,
),
selected: selected,
child: AspectRatio(
aspectRatio: kAspectRatio,
child: Stack(
children: [
child,
Positioned(
top: 0,
right: 0,
left: 0,
child: SizedBox(
child: processThumbnail.isRefreshing
? const LinearProgressIndicator(
minHeight: 3,
backgroundColor: Colors.transparent,
)
: null,
),
Positioned(
right: 0,
bottom: 0,
child: Container(
padding: const EdgeInsets.fromLTRB(12, 4, 12, 4),
margin: const EdgeInsets.all(1),
color: Colors.black.withOpacity(0.5),
child: Text(
'${index + 1}',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
Positioned(
right: 0,
bottom: 0,
child: Container(
padding: const EdgeInsets.fromLTRB(12, 4, 12, 4),
margin: const EdgeInsets.all(1),
color: Colors.black.withOpacity(0.5),
child: Text(
'$page',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
],
),
),
),
@@ -108,67 +102,36 @@ class SlideThumbnail extends HookWidget {
}
}

class SlideThumbnailDynamic<T extends Slide> extends StatelessWidget {
final bool selected;
final VoidCallback onTap;
final int index;
final T slide;

const SlideThumbnailDynamic({
super.key,
required this.selected,
required this.index,
required this.onTap,
required this.slide,
});

@override
Widget build(BuildContext context) {
final selectedColor = selected ? Colors.blue : Colors.transparent;

return GestureDetector(
onTap: onTap,
child: _PreviewContainer(
selectedColor: selectedColor,
child: AbsorbPointer(
child: AspectRatio(
aspectRatio: kAspectRatio,
child: ScaledWidget(
child: SlideView(slide),
),
),
),
),
);
}
}

class _PreviewContainer extends StatelessWidget {
final Color selectedColor;
final Widget child;
final bool selected;

const _PreviewContainer({
required this.selectedColor,
required this.selected,
required this.child,
});

@override
Widget build(BuildContext context) {
final style = Style(
$box.color.grey.shade900(),
$box.color.$neutral(2),
$box.margin.all(8),
$box.border.width(2),
$box.shadow(
color: Colors.black.withOpacity(0.5),
blurRadius: 4,
spreadRadius: 1,
),
);

selected ? $box.wrap.scale(1.05) : $box.wrap.scale(1),
selected ? $box.wrap.opacity(1) : $box.wrap.opacity(0.5),
selected ? $box.border.color.$accent() : $box.border.color.transparent(),
// $on.hover(
// $box.wrap.opacity(1),
// ),
).animate();

return Box(
style: style.add(
$box.border.color(selectedColor),
),
style: style,
child: child,
);
}
68 changes: 68 additions & 0 deletions packages/superdeck/lib/components/molecules/navigation_rail.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:mix/mix.dart';

import '../remix/button.dart';

class CustomNavigationRail extends HookWidget {
final int selectedIndex;
final ValueChanged<int>? onDestinationSelected;
final List<CustomNavigationRailDestination> destinations;
final bool displayLabel;
final double? leading;
final double? trailing;

CustomNavigationRail({
required this.selectedIndex,
this.onDestinationSelected,
required this.destinations,
this.displayLabel = false,
this.leading,
this.trailing,
});

@override
Widget build(BuildContext context) {
final _buildDestination = useCallback((int index) {
final destination = destinations[index];
final isSelected = selectedIndex == index;

return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: SDIconButton(
icon: destination.icon,
onPressed: () => onDestinationSelected?.call(index),
selected: isSelected,
),
);
}, [selectedIndex, destinations]);

return VBox(
style: _containerStyle,
children: [
if (leading != null) SizedBox(height: leading),
for (int i = 0; i < destinations.length; i++) _buildDestination(i),
if (trailing != null) SizedBox(height: trailing),
],
);
}
}

get _containerStyle => Style(
$box.color.black(),
$box.padding(16),
$box.border.right(
color: Colors.white10,
width: 1,
),
);

class CustomNavigationRailDestination {
final IconData icon;
final String label;

CustomNavigationRailDestination({
required this.icon,
required this.label,
});
}
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ class SlidePreview<T extends Slide> extends StatelessWidget {
return Center(
child: Container(
decoration: BoxDecoration(
color: const Color.fromARGB(144, 0, 0, 0),
color: Colors.black,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.3),
Original file line number Diff line number Diff line change
@@ -57,49 +57,28 @@ class SlideThumbnailList extends HookWidget {
return;
}, [currentPage, slides]);

return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(0),
child: Container(
height: 30,
color: Colors.black,
),
),
bottomNavigationBar: BottomNavigationBar(
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.arrow_back),
label: 'Previous',
),
BottomNavigationBarItem(
icon: Icon(Icons.arrow_forward),
label: 'Next',
),
],
onTap: (index) {
if (index == 0) {
navigation.goToPage(currentPage - 1);
} else {
navigation.goToPage(currentPage + 1);
}
},
),
body: Container(
color: const Color.fromARGB(108, 0, 0, 0),
child: ScrollablePositionedList.builder(
scrollDirection: context.isSmall ? Axis.horizontal : Axis.vertical,
itemCount: slides.length,
itemPositionsListener: controller.itemPositionsListener,
itemScrollController: controller.itemScrollController,
padding: const EdgeInsets.all(20),
itemBuilder: (context, index) {
return SlideThumbnail(
index: index,
return Container(
color: Colors.black,
child: ScrollablePositionedList.builder(
scrollDirection: context.isSmall ? Axis.horizontal : Axis.vertical,
itemCount: slides.length,
itemPositionsListener: controller.itemPositionsListener,
itemScrollController: controller.itemScrollController,
padding: const EdgeInsets.all(20),
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 8,
horizontal: 10,
),
child: SlideThumbnail(
page: index + 1,
selected: currentPage == index,
onTap: () => navigation.goToPage(index),
slide: slides[index],
);
}),
),
),
);
}),
);
}
}
65 changes: 37 additions & 28 deletions packages/superdeck/lib/components/molecules/split_view.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';

import '../../helpers/hooks.dart';
import '../../helpers/utils.dart';
import '../../superdeck.dart';
import '../atoms/sized_transition.dart';
import 'navigation_rail.dart';
import 'slide_thumbnail_list.dart';

final _valueKey = GlobalKey();

class SplitView extends HookWidget {
final Widget child;

@@ -17,14 +17,20 @@ class SplitView extends HookWidget {
required this.child,
});

final _maxWidth = 450.0;
final _minWidth = 300.0;
final _maxWidth = 400.0;

@override
Widget build(BuildContext context) {
final navigation = useNavigation();
final sideSize = useState(context.isMobileLandscape ? 200.0 : _maxWidth);
final isDragging = useState(false);
final location = useRouteLocation();

final locationIndex = switch (location) {
'/' => 0,
'/export' => 1,
'/settings' => 2,
_ => 0,
};

final animationController = useAnimationController(
duration: Durations.medium1,
@@ -43,11 +49,6 @@ class SplitView extends HookWidget {
}
}, [navigation.sideIsOpen]);

final handleUpdateSize = useCallback((DragUpdateDetails details) {
sideSize.value =
(sideSize.value + details.delta.dx).clamp(_minWidth, _maxWidth);
}, [sideSize.value]);

const sideHeight = 200.0;

final isSmall = context.isSmall || context.isMobileLandscape;
@@ -64,23 +65,6 @@ class SplitView extends HookWidget {
padding = EdgeInsets.only(left: animatedWidth);
}

final divider = MouseRegion(
cursor: SystemMouseCursors.resizeColumn,
child: GestureDetector(
onHorizontalDragUpdate: handleUpdateSize,
onHorizontalDragStart: (_) {
isDragging.value = true;
},
onHorizontalDragEnd: (_) {
isDragging.value = false;
},
child: Container(
color: Colors.black,
width: 8,
),
),
);

final drawer = Align(
alignment: isSmall ? Alignment.bottomCenter : Alignment.centerLeft,
child: SizedTransition(
@@ -90,10 +74,35 @@ class SplitView extends HookWidget {
height: isSmall ? sideHeight : null,
child: Row(
children: [
CustomNavigationRail(
selectedIndex: locationIndex,
onDestinationSelected: (value) {
return switch (value) {
0 => context.go('/'),
1 => context.push('/export'),
2 => context.go('/settings'),
_ => null,
};
},
leading: 20,
destinations: [
CustomNavigationRailDestination(
icon: Icons.view_carousel,
label: 'Home',
),
CustomNavigationRailDestination(
icon: Icons.save_alt,
label: 'Export',
),
CustomNavigationRailDestination(
icon: Icons.settings,
label: 'Settings',
),
],
),
const Expanded(
child: SlideThumbnailList(),
),
divider,
],
),
),
61 changes: 5 additions & 56 deletions packages/superdeck/lib/components/organisms/app_shell.dart
Original file line number Diff line number Diff line change
@@ -3,52 +3,29 @@ import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';

import '../../helpers/hooks.dart';
import '../../helpers/utils.dart';
import '../../superdeck.dart';
import '../molecules/split_view.dart';

final scaffoldKey = GlobalKey<ScaffoldState>();

/// Builds the "shell" for the app by building a Scaffold with a
/// BottomNavigationBar, where [child] is placed in the body of the Scaffold.
class ScaffoldWithNavBar extends HookWidget {
/// Constructs an [ScaffoldWithNavBar].
const ScaffoldWithNavBar({
class AppShell extends HookWidget {
const AppShell({
required this.navigationShell,
super.key = const ValueKey<String>('ScaffoldWithNavBar'),
});

/// The navigation shell and container for the branch Navigators.
final StatefulNavigationShell navigationShell;

void _onTap(BuildContext context, int index) {
// When navigating to a new branch, it's recommended to use the goBranch
// method, as doing so makes sure the last navigation state of the
// Navigator for the branch is restored.
navigationShell.goBranch(
index,
// A common pattern when using bottom navigation bars is to support
// navigating to the initial location when tapping the item that is
// already active. This example demonstrates how to support this behavior,
// using the initialLocation parameter of goBranch.
// initialLocation: index == navigationShell.currentIndex,
);
}

@override
Widget build(BuildContext context) {
final isSmall = context.isSmall;
final navigation = useNavigation();
final slides = useSlides();
final invalidSlides = slides.whereType<InvalidSlide>().toList();
final animationController = useAnimationController(
duration: Durations.short3,
);

final animation = useAnimation(CurvedAnimation(
parent: animationController,
curve: Curves.ease,
));

final handlePrevious = useCallback(() {
if (navigation.page == 0) return;
@@ -68,14 +45,6 @@ class ScaffoldWithNavBar extends HookWidget {
}
}, [navigation.sideIsOpen]);

usePostFrameEffect(() {
if (navigation.sideIsOpen) {
animationController.forward();
} else {
animationController.reverse();
}
}, [navigation.sideIsOpen]);

final bindings = {
const SingleActivator(
LogicalKeyboardKey.arrowRight,
@@ -97,30 +66,10 @@ class ScaffoldWithNavBar extends HookWidget {
return CallbackShortcuts(
bindings: bindings,
child: Scaffold(
backgroundColor: const Color.fromARGB(255, 9, 9, 9),
bottomNavigationBar: null,
extendBodyBehindAppBar: true,
extendBody: true,
appBar: AppBar(
backgroundColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
toolbarOpacity: animation,
actions: [
IconButton(
onPressed: () => context.go('/export'),
icon: const Icon(
Icons.picture_as_pdf,
),
),
IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: handlePrevious,
),
IconButton(
icon: const Icon(Icons.arrow_forward),
onPressed: handleNext,
),
],
),
key: scaffoldKey,
floatingActionButtonLocation: isSmall
? FloatingActionButtonLocation.miniEndFloat
@@ -133,7 +82,7 @@ class ScaffoldWithNavBar extends HookWidget {
child: const Icon(Icons.menu),
),
),
body: navigationShell,
body: SplitView(child: navigationShell),
),
);
}
52 changes: 52 additions & 0 deletions packages/superdeck/lib/components/remix/button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:remix/remix.dart';

class SDButton extends StatelessWidget {
const SDButton({
required this.onPressed,
super.key,
required this.label,
this.icon,
});

final VoidCallback onPressed;
final String label;

final IconData? icon;

@override
Widget build(BuildContext context) {
return RxButton(
onPressed: onPressed,
type: ButtonVariant.surface,
iconLeft: icon,
label: '',
);
}
}

class SDIconButton extends StatelessWidget {
const SDIconButton({
required this.onPressed,
super.key,
required this.icon,
this.selected = false,
});

final VoidCallback onPressed;
final bool selected;

final IconData icon;

@override
Widget build(BuildContext context) {
return RxButton(
onPressed: onPressed,
type: selected ? ButtonVariant.surface : ButtonVariant.ghost,
iconLeft: icon,
size: ButtonSize.large,
label: '',
);
}
}
Empty file.
133 changes: 39 additions & 94 deletions packages/superdeck/lib/components/superdeck_app.dart
Original file line number Diff line number Diff line change
@@ -3,19 +3,17 @@ import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:localstorage/localstorage.dart';
import 'package:remix/remix.dart';
import 'package:window_manager/window_manager.dart';

import '../../helpers/syntax_highlighter.dart';
import '../../superdeck.dart';
import '../helpers/constants.dart';
import '../helpers/routes.dart';
import '../helpers/theme.dart';
import '../providers/examples_provider.dart';
import '../providers/snapshot_provider.dart';
import '../providers/style_provider.dart';
import '../screens/export_screen.dart';
import '../screens/home_screen.dart';
import 'atoms/loading_indicator.dart';

final _uniqueKey = UniqueKey();
@@ -40,7 +38,8 @@ class SuperDeckApp extends HookWidget {
WidgetsFlutterBinding.ensureInitialized();

await Future.wait([
initLocalStorage(),
SuperDeckController.initialize(),
NavigationController.initialize(),
SyntaxHighlight.initialize(),
_initializeWindowManager(),
]);
@@ -50,47 +49,41 @@ class SuperDeckApp extends HookWidget {

@override
Widget build(BuildContext context) {
return Theme(
data: theme,
child: FutureBuilder(
future: SuperDeckApp.initialize(),
builder: (context, snapshot) {
return StyleProvider(
baseStyle: baseStyle,
styles: styles,
child: ExamplesProvider(
examples: examples,
child: ListenableBuilder(
listenable: $superdeck,
builder: (context, snapshot) {
return MixTheme(
data: MixThemeData.withMaterial(),
child: MaterialApp.router(
debugShowCheckedModeBanner: false,
title: 'Superdeck',
routerConfig: _router,
theme: Theme.of(context),
builder: (context, child) {
return SnapshotProvider(
child: LoadingOverlay(
isLoading: $superdeck.loading,
key: _uniqueKey,
child: Builder(
builder: (context) {
return $superdeck.completed
? child!
: const SizedBox();
},
),
),
);
},
),
);
}),
),
);
}),
return FutureBuilder(
future: SuperDeckApp.initialize(),
builder: (context, snapshot) {
return StyleProvider(
baseStyle: baseStyle,
styles: styles,
child: ExamplesProvider(
examples: examples,
child: ListenableBuilder(
listenable: $superdeck,
builder: (context, snapshot) {
return RemixTokens(
data: RemixTokens.dark,
child: MaterialApp.router(
debugShowCheckedModeBanner: true,
title: 'Superdeck',
routerConfig: goRouterConfig,
theme: theme,
builder: (context, child) {
return SnapshotProvider(
child: LoadingOverlay(
isLoading: $superdeck.loading,
key: _uniqueKey,
child: $superdeck.completed
? child!
: const SizedBox(),
),
);
},
),
);
}),
),
);
},
);
}
}
@@ -116,51 +109,3 @@ Future<void> _initializeWindowManager() async {

await windowManager.setAspectRatio(kAspectRatio);
}

final _rootNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'root');
final _sectionANavigatorKey =
GlobalKey<NavigatorState>(debugLabel: 'sectionANav');

final _router = GoRouter(
navigatorKey: _rootNavigatorKey,
initialLocation: '/',
routes: <RouteBase>[
StatefulShellRoute.indexedStack(
builder: (
BuildContext context,
GoRouterState state,
StatefulNavigationShell navigationShell,
) {
// Return the widget that implements the custom shell (in this case
// using a BottomNavigationBar). The StatefulNavigationShell is passed
// to be able access the state of the shell and to navigate to other
// branches in a stateful way.
return ScaffoldWithNavBar(navigationShell: navigationShell);
},
branches: <StatefulShellBranch>[
// The route branch for the first tab of the bottom navigation bar.
StatefulShellBranch(
navigatorKey: _sectionANavigatorKey,
routes: <RouteBase>[
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) {
return const HomeScreen();
},
),
],
),
StatefulShellBranch(
routes: <RouteBase>[
GoRoute(
path: '/export',
builder: (BuildContext context, GoRouterState state) {
return const ExportScreen();
},
),
],
),
],
),
],
);
42 changes: 42 additions & 0 deletions packages/superdeck/lib/helpers/dialog_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import 'package:flutter/material.dart';

/// A dialog page with Material entrance and exit animations, modal barrier color,
/// and modal barrier behavior (dialog is dismissible with a tap on the barrier).
class DialogPage<T> extends Page<T> {
final Offset? anchorPoint;
final Color? barrierColor;
final bool barrierDismissible;
final String? barrierLabel;
final bool useSafeArea;
final CapturedThemes? themes;
final WidgetBuilder builder;

const DialogPage({
required this.builder,
this.anchorPoint,
this.barrierColor = Colors.black54,
this.barrierDismissible = true,
this.barrierLabel,
this.useSafeArea = true,
this.themes,
super.key,
super.name,
super.arguments,
super.restorationId,
});

@override
Route<T> createRoute(BuildContext context) {
return DialogRoute<T>(
context: context,
settings: this,
builder: builder,
anchorPoint: anchorPoint,
barrierColor: barrierColor,
barrierDismissible: barrierDismissible,
barrierLabel: barrierLabel,
useSafeArea: useSafeArea,
themes: themes,
);
}
}
19 changes: 19 additions & 0 deletions packages/superdeck/lib/helpers/hooks.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';

void useMount(VoidCallback fn) {
@@ -113,3 +114,21 @@ T? useDistinct<T>(T value, [Predicate<T>? compare]) {
}

typedef Predicate<T> = bool Function(T prev, T next);

GoRouter useGoRouter() {
final context = useContext();
return GoRouter.of(context);
}

String useRouteLocation() {
final router = useGoRouter();
final uri = useState(router.routeInformationProvider.value.uri);

useEffect(() {
router.routerDelegate.addListener(() {
uri.value = router.routeInformationProvider.value.uri;
});
}, [router.routerDelegate]);

return uri.value.toString();
}
68 changes: 68 additions & 0 deletions packages/superdeck/lib/helpers/routes.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:go_router_paths/go_router_paths.dart';

import '../../superdeck.dart';
import '../helpers/dialog_page.dart';
import '../screens/export_screen.dart';
import '../screens/presentation_screen.dart';

class SDPaths {
static Path get presentation => Path('/');
static ExportPath get export => ExportPath();

static Param<Param> get slides => Param('slides', 'page');
}

class ExportPath extends Path<ExportPath> {
ExportPath() : super('export');

Path get low => Path('low', parent: this);
Path get good => Path('good', parent: this);
Path get better => Path('better', parent: this);
Path get best => Path('best', parent: this);
}

final _rootNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'root');
final _sectionANavigatorKey =
GlobalKey<NavigatorState>(debugLabel: 'sectionANav');

final goRouterConfig = GoRouter(
navigatorKey: _rootNavigatorKey,
initialLocation: '/',
routes: <RouteBase>[
StatefulShellRoute.indexedStack(
builder: (context, state, navigationShell) {
return AppShell(navigationShell: navigationShell);
},
branches: [
// The route branch for the first tab of the bottom navigation bar.
StatefulShellBranch(
navigatorKey: _sectionANavigatorKey,
routes: [
GoRoute(
path: SDPaths.presentation.goRoute,
builder: (context, state) => const PresentationScreen(),
),
],
),
StatefulShellBranch(
routes: [
GoRoute(
path: SDPaths.export.goRoute,
pageBuilder: (context, state) {
return DialogPage(
builder: (_) => Dialog(
child: ExportScreen(),
),
barrierColor: Colors.black54, // Optional, as it's the default
barrierDismissible: true, // Optional, as it's the default
);
},
),
],
),
],
),
],
);
6 changes: 4 additions & 2 deletions packages/superdeck/lib/helpers/theme.dart
Original file line number Diff line number Diff line change
@@ -2,9 +2,11 @@ import 'package:flutter/material.dart';

ThemeData get theme => ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.cyan,
seedColor: Colors.black,
dynamicSchemeVariant: DynamicSchemeVariant.fidelity,
contrastLevel: 1,
error: Colors.red,
onTertiary: Colors.orange,
onTertiary: Colors.cyan,
brightness: Brightness.dark,
),
);
19 changes: 15 additions & 4 deletions packages/superdeck/lib/providers/controller.dart
Original file line number Diff line number Diff line change
@@ -8,13 +8,22 @@ import '../models/asset_model.dart';
import '../models/slide_model.dart';
import '../services/reference_service.dart';

final $superdeck = SuperDeckController();
final $superdeck = SuperDeckController.instance;

class SuperDeckController extends ChangeNotifier {
SuperDeckController() {
_loadData();
ReferenceService.instance.listen(_loadData);
SuperDeckController._();

static final instance = SuperDeckController._();

bool _initialized = false;

static Future<void> initialize() async {
if (instance._initialized) return;
instance._initialized = true;
await instance._loadData();
ReferenceService.instance.listen(instance._loadData);
}

bool _loading = false;
Object? _error;
List<Slide> _slides = [];
@@ -96,6 +105,8 @@ class NavigationController extends ChangeNotifier {

static final instance = NavigationController._();

static Future<void> initialize() => initLocalStorage();

int _page = 0;
bool _sideIsOpen = false;
int _screen = 0;
49 changes: 24 additions & 25 deletions packages/superdeck/lib/screens/export_screen.dart
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import 'package:file_saver/file_saver.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;

@@ -78,32 +79,30 @@ class ExportScreen extends HookWidget {
}).toList();
}

return Scaffold(
appBar: AppBar(
title: const Text('Export'),
),
body: Center(
child: SizedBox(
width: 300,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Select Quality:',
style: TextStyle(
fontSize: 16.0,
),
),
...buildRadioList(),
const SizedBox(height: 24.0),
ElevatedButton(
onPressed: convertToPdf,
child: const Text('Save'),
),
],
return SizedBox(
width: 300,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Select Quality:',
style: TextStyle(
fontSize: 16.0,
),
),
),
...buildRadioList(),
const SizedBox(height: 24.0),
ElevatedButton(
onPressed: convertToPdf,
child: const Text('Save'),
),
ElevatedButton(
onPressed: () {
context.pop();
},
child: Text('Close'))
],
),
);
}
Original file line number Diff line number Diff line change
@@ -2,12 +2,11 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

import '../components/molecules/slide_preview.dart';
import '../components/molecules/split_view.dart';
import '../helpers/hooks.dart';
import '../superdeck.dart';

class HomeScreen extends HookWidget {
const HomeScreen({super.key});
class PresentationScreen extends HookWidget {
const PresentationScreen({super.key});

final _duration = const Duration(milliseconds: 300);
final _curve = Curves.easeInOutCubic;
@@ -27,15 +26,13 @@ class HomeScreen extends HookWidget {
);
}, [page]);

return SplitView(
child: Center(
child: PageView.builder(
controller: pageController,
itemCount: slides.length,
itemBuilder: (_, index) {
return SlidePreview(slides[index]);
},
),
return Center(
child: PageView.builder(
controller: pageController,
itemCount: slides.length,
itemBuilder: (_, index) {
return SlidePreview(slides[index]);
},
),
);
}
6 changes: 6 additions & 0 deletions packages/superdeck/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@ description: Presentation slides for Flutter with Flutter
version: 0.0.4
homepage: https://github.com/leoafarias/superdeck

publish_to: none

environment:
sdk: ">=3.3.0 <4.0.0"

@@ -34,6 +36,10 @@ dependencies:
web: ^0.5.1
file_saver: ^0.2.13
render: ^0.0.1
remix:
path: ../../../mix/packages/remix
go_router_paths: ^0.2.2


dev_dependencies:
flutter_test:
11 changes: 11 additions & 0 deletions packages/superdeck/pubspec_overrides.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
dependency_overrides:
mix:
path: ../../../mix/packages/mix
mix_generator:
path: ../../../mix/packages/mix_generator
mix_lint:
path: ../../../mix/packages/mix_lint
mix_annotations:
path: ../../../mix/packages/mix_annotations
remix:
path: ../../../mix/packages/remix

0 comments on commit 9e19286

Please sign in to comment.