Skip to content

Commit

Permalink
Merge pull request #79 from victoreronmosele/state-history
Browse files Browse the repository at this point in the history
Implement State History (Undo, Redo, Version History)
  • Loading branch information
victoreronmosele authored Nov 11, 2024
2 parents 08fed76 + ad944e8 commit d33f6ba
Show file tree
Hide file tree
Showing 31 changed files with 1,017 additions and 378 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/firebase-hosting-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
java-version: "12.x"
- uses: subosito/flutter-action@v1
with:
flutter-version: "3.16.9"
flutter-version: "3.24.0"
channel: stable
- run: echo $FIREBASE_CONFIG | base64 -d > lib/firebase_options.dart
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/firebase-hosting-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
java-version: "12.x"
- uses: subosito/flutter-action@v1
with:
flutter-version: "3.16.9"
flutter-version: "3.24.0"
channel: stable
- run: echo $FIREBASE_CONFIG | base64 -d > lib/firebase_options.dart
env:
Expand Down
1 change: 1 addition & 0 deletions devtools_options.yaml
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
extensions:
- provider: true
1 change: 1 addition & 0 deletions lib/data/app_colors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class AppColors {

static const toolBar = Color(0xff2c2c2c);
static const toolBarIcon = white;
static final toolBarIconDisabled = white.withOpacity(0.38);
static final toolBarIconHover = white.withOpacity(0.08);
static final toolBarIconFocus = white.withOpacity(0.12);

Expand Down
1 change: 1 addition & 0 deletions lib/data/app_dimensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class AppDimensions extends InheritedWidget {
double get toolBarHeight => 48;

double get chooseRandomGradientIconButtonSize => 16;
double get versionHistoryCloseIconButtonSize => 16;

double get sampleTitleBottomMargin => 2.0;

Expand Down
21 changes: 21 additions & 0 deletions lib/data/app_strings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,27 @@ class AppStrings {
static const alignmentY = 'Y:';
static const alignment = 'Alignment:';
static const endAlignment = 'End Alignment:';
static const noActionsToUndo = 'No actions to undo';
static const noActionsToRedo = 'No actions to redo';
static const undo = 'Undo';
static const redo = 'Redo';
static const versionHistory = 'Version History';

static const macCommandKey = '⌘';
static const macShiftKey = '⇧';
static const windowsControlKey = 'Ctrl';

/// Returns:
/// * Undo ⌘Z, for macOs
/// * Undo CtrlZ, otherwise
static String getUndoShortcutText({required bool isMac}) =>
'${isMac ? macCommandKey : windowsControlKey}Z';

/// Returns:
/// * Redo ⌘⇧Z, for macOs
/// * Redo CtrlY, otherwise
static String getRedoShortcutText({required bool isMac}) =>
isMac ? '$macCommandKey${macShiftKey}Z' : '${windowsControlKey}Y';

/// URLs
static const githubUrl =
Expand Down
14 changes: 11 additions & 3 deletions lib/data/app_typedefs.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import 'package:flutter/material.dart';
import 'package:flutter_gradient_generator/models/abstract_gradient.dart';

/// [Stop] is an integer representing a Gradient color stop.
/// An integer representing a Gradient color stop.
///
/// It is a value between 0 and 100.
typedef Stop = int;

/// [ColorAndStop] is a record that holds a [Color] and a [Stop].
/// A record that holds a [Color] and a [Stop].
typedef ColorAndStop = ({
Color color,
Stop stop,
});

/// [FlutterGradientConverter] is a function that converts [colors] and optional
/// A function that converts [colors] and optional
/// [stops] to a [Gradient] from Flutter's painting library.
typedef FlutterGradientConverter = Gradient Function(
{required List<Color> colors, List<double>? stops});

/// A record that holds a [Gradient] and a [DateTime] timestamp when the
/// gradient was generated.
typedef TimeStampedGradient = ({
AbstractGradient gradient,
DateTime timeStamp,
});
136 changes: 108 additions & 28 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gradient_generator/data/app_dimensions.dart';
import 'package:flutter_gradient_generator/data/app_fonts.dart';
import 'package:flutter_gradient_generator/data/app_strings.dart';
import 'package:flutter_gradient_generator/firebase_options.dart';
import 'package:flutter_gradient_generator/ui/screens/home_screen.dart';
import 'package:flutter_gradient_generator/utils/analytics.dart';
import 'package:flutter_gradient_generator/utils/gradient_downloader.dart';
import 'package:flutter_gradient_generator/utils/platform_checker.dart';
import 'package:flutter_gradient_generator/view_models/gradient_view_model.dart';
import 'package:flutter_gradient_generator/view_models/history_view_model.dart';
import 'package:flutter_gradient_generator/view_models/home_view_model.dart';
import 'package:provider/provider.dart';
import 'package:url_strategy/url_strategy.dart';

Expand All @@ -21,51 +26,126 @@ void main() async {
}

class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
const MyApp({super.key});

@override
State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
late final GradientViewModel gradientViewModel;
late final HistoryViewModel historyViewModel;
late final HomeViewModel homeViewModel;
late final Analytics analytics;
late final GradientDownloader gradientDownloader;
late final PlatformChecker platformChecker;

@override
void initState() {
super.initState();

gradientViewModel = GradientViewModel();
gradientViewModel = GradientViewModel(
onNewGradientSet: (gradient) {
historyViewModel.addNewGradientToHistory(gradient);
},
);
historyViewModel = HistoryViewModel(
onUndoOrRedo: () {
final lastGradient = historyViewModel.liveHistory.lastOrNull;

if (lastGradient == null) {
gradientViewModel.setGradientToDefault(isNewGradient: false);
} else {
gradientViewModel.setGradientDetails(
gradientToSet: lastGradient, isNewGradient: false);
}
},
);
homeViewModel = HomeViewModel();
analytics = Analytics();
gradientDownloader = GradientDownloader();
platformChecker = PlatformChecker();
}

@override
Widget build(BuildContext context) {
return OrientationBuilder(builder: (context, orientation) {
final screenSize = MediaQuery.of(context).size;

final width = screenSize.width;
final height = screenSize.height;

return MaterialApp(
title: AppStrings.appTitle,
debugShowCheckedModeBanner: false,
theme: ThemeData(textTheme: AppFonts.getTextTheme(context)),
home: AppDimensions(
orientation: orientation,
screenWidth: width,
screenHeight: height,
child: MultiProvider(
providers: [
ChangeNotifierProvider.value(
value: gradientViewModel,
),
Provider.value(
value: Analytics(),
return OrientationBuilder(
builder: (context, orientation) {
final screenSize = MediaQuery.of(context).size;

final width = screenSize.width;
final height = screenSize.height;

return MaterialApp(
title: AppStrings.appTitle,
debugShowCheckedModeBanner: false,
theme: ThemeData(textTheme: AppFonts.getTextTheme(context)),
home: AppDimensions(
orientation: orientation,
screenWidth: width,
screenHeight: height,
child: MultiProvider(
providers: [
ChangeNotifierProvider.value(
value: gradientViewModel,
),
ChangeNotifierProvider.value(
value: historyViewModel,
),
ChangeNotifierProvider.value(
value: homeViewModel,
),
Provider.value(
value: analytics,
),
Provider.value(
value: gradientDownloader,
),
Provider.value(
value: platformChecker,
),
],
child: CallbackShortcuts(
bindings: () {
final isMac = platformChecker.isMac();

// Command + Z if Mac, Control + Z otherwise
final undoShortcutActivator = SingleActivator(
LogicalKeyboardKey.keyZ,
meta: isMac,
control: !isMac,
);

// Command + Shift + Z if Mac, Control + Y otherwise
final redoShortcutActivator = SingleActivator(
isMac ? LogicalKeyboardKey.keyZ : LogicalKeyboardKey.keyY,
meta: isMac,
control: !isMac,
shift: isMac,
);

return {
undoShortcutActivator: () {
analytics.logUndoShortcutPressedEvent();

historyViewModel.undo();
},
redoShortcutActivator: () {
analytics.logRedoShortcutPressedEvent();

historyViewModel.redo();
},
};
}(),
child: Focus(
autofocus: true,
child: const HomeScreen(),
),
),
],
child: const HomeScreen(),
),
),
),
);
});
);
},
);
}
}
7 changes: 1 addition & 6 deletions lib/models/linear_style_gradient.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,7 @@ import 'package:quiver/core.dart';
// ignore: must_be_immutable
class LinearStyleGradient extends AbstractGradient {
LinearStyleGradient(
{required List<ColorAndStop> colorAndStopList,
required GradientDirection gradientDirection})
: super(
colorAndStopList: colorAndStopList,
gradientDirection: gradientDirection,
);
{required super.colorAndStopList, required super.gradientDirection});

String get _widgetStringTemplate => '''LinearGradient(
colors: ${getColorList()},
Expand Down
7 changes: 1 addition & 6 deletions lib/models/radial_style_gradient.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,7 @@ import 'package:quiver/core.dart';
// ignore: must_be_immutable
class RadialStyleGradient extends AbstractGradient {
RadialStyleGradient(
{required List<ColorAndStop> colorAndStopList,
required GradientDirection gradientDirection})
: super(
colorAndStopList: colorAndStopList,
gradientDirection: gradientDirection,
);
{required super.colorAndStopList, required super.gradientDirection});

String get _widgetStringTemplate => '''RadialGradient(
colors: ${getColorList()},
Expand Down
7 changes: 1 addition & 6 deletions lib/models/sweep_style_gradient.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,7 @@ import 'package:quiver/core.dart';
// ignore: must_be_immutable
class SweepStyleGradient extends AbstractGradient {
SweepStyleGradient(
{required List<ColorAndStop> colorAndStopList,
required GradientDirection gradientDirection})
: super(
colorAndStopList: colorAndStopList,
gradientDirection: gradientDirection,
);
{required super.colorAndStopList, required super.gradientDirection});

String get _widgetStringTemplate => '''SweepGradient(
colors: ${getColorList()},
Expand Down
Loading

0 comments on commit d33f6ba

Please sign in to comment.