diff --git a/.github/workflows/firebase-hosting-merge.yml b/.github/workflows/firebase-hosting-merge.yml index 2e2731c..f88db83 100644 --- a/.github/workflows/firebase-hosting-merge.yml +++ b/.github/workflows/firebase-hosting-merge.yml @@ -15,13 +15,13 @@ jobs: java-version: "12.x" - uses: subosito/flutter-action@v1 with: - flutter-version: "3.13.6" + flutter-version: "3.16.9" channel: stable - run: echo $FIREBASE_CONFIG | base64 -d > lib/firebase_options.dart env: FIREBASE_CONFIG: ${{ secrets.FIREBASE_CONFIG }} - run: flutter pub get - - run: flutter build web --release --no-tree-shake-icons + - run: flutter build web --release - uses: FirebaseExtended/action-hosting-deploy@v0 with: repoToken: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/firebase-hosting-pull-request.yml b/.github/workflows/firebase-hosting-pull-request.yml index ba0ce2d..e4b0a00 100644 --- a/.github/workflows/firebase-hosting-pull-request.yml +++ b/.github/workflows/firebase-hosting-pull-request.yml @@ -13,13 +13,13 @@ jobs: java-version: "12.x" - uses: subosito/flutter-action@v1 with: - flutter-version: "3.13.6" + flutter-version: "3.16.9" channel: stable - run: echo $FIREBASE_CONFIG | base64 -d > lib/firebase_options.dart env: FIREBASE_CONFIG: ${{ secrets.FIREBASE_CONFIG }} - run: flutter pub get - - run: flutter build web --release --no-tree-shake-icons + - run: flutter build web --release - uses: FirebaseExtended/action-hosting-deploy@v0 with: repoToken: '${{ secrets.GITHUB_TOKEN }}' diff --git a/.metadata b/.metadata index c1ee81b..777c837 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,27 @@ # This file should be version controlled and should not be manually edited. version: - revision: 1d9032c7e1d867f071f2277eb1673e8f9b0274e3 - channel: stable + revision: "41456452f29d64e8deb623a3c927524bcf9f111b" + channel: "stable" project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b + base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b + - platform: web + create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b + base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/README.md b/README.md index 8c3a49e..102a9b6 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,18 @@ Flutter Gradient Generator is a web app that generates linear, radial and sweep ## Usage -1. Visit the [web app](https://fluttergradientgenerator.com/). +1. **Visit the [web app](https://fluttergradientgenerator.com/).** -2. Choose the gradient style. The options are: +2. **Choose the gradient style.** + + The options are: - linear - radial - sweep -3. Choose the gradient direction. The options are: +3. **Choose the gradient direction.** + + The options are: - top-left - top-center - top-right @@ -27,14 +31,18 @@ Flutter Gradient Generator is a web app that generates linear, radial and sweep - bottom-center - bottom-right -4. Choose the gradient colors. You can either +4. **Choose the gradient colors.** + + You can: - use the color pickers to select your colors, - - generate random colors by clicking the "Random" button or - - add more colors by clicking the "Add Color" button. + - click on a gradient sample to use the sample's colors, + - use a random gradient sample by clicking on the shuffle button, + - add more colors by clicking the + button. -5. Enter the color stops. +5. **Enter the color stops.** + +6. **Click on "Copy Gradient Code" and the code will be copied to your clipboard.** -6. Click on "Copy Gradient Code" and the code will be copied to your clipboard. ## Running 1. Clone the repository @@ -61,19 +69,20 @@ Flutter Gradient Generator is a web app that generates linear, radial and sweep - [x] Color picker - [x] Color stops - [x] Addition of more colors -- [ ] Addition of more gradient styles +- [x] Gradient samples +- [x] Downloading gradient as image +- [ ] Addition of more gradient styles +- [ ] Text gradients - [ ] CSS to Flutter converter - [ ] Dark mode ## Contact -Victor Eronmosele - victoreronmosele@gmail.com - Project Link: [https://github.com/victoreronmosele/flutter_gradient_generator](https://github.com/victoreronmosele/flutter_gradient_generator) ## Acknowledgments -* [CSS Gradient Generator](https://www.css-gradient.com/) for the visual design inspiration. +* [Gradient samples](https://github.com/ghosh/uiGradients/blob/master/gradients.json) are from [uiGradients](https://uigradients.com/). ## License diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 0000000..7e7e7f6 --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1 @@ +extensions: diff --git a/lib/data/app_colors.dart b/lib/data/app_colors.dart index 33e6868..b075155 100644 --- a/lib/data/app_colors.dart +++ b/lib/data/app_colors.dart @@ -22,4 +22,18 @@ class AppColors { (color: _initialGradientEndColor, stop: _initialGradientEndStop) ]; } + + //TODO: Make this dependent on the theme and set it to + //[_previewBackgroundLight] or [_previewBackgroundDark] accordingly + static const previewBackground = _previewBackgroundLight; + static const _previewBackgroundLight = Color(0xfff5f5f5); + // ignore: unused_field + static const _previewBackgroundDark = Color(0xff1e1e1e); + + static const toolBar = Color(0xff2c2c2c); + static const toolBarIcon = white; + static final toolBarIconHover = white.withOpacity(0.08); + static final toolBarIconFocus = white.withOpacity(0.12); + + static final colorPickerBorder = grey; } diff --git a/lib/data/app_dimensions.dart b/lib/data/app_dimensions.dart index efad11e..a604ed7 100644 --- a/lib/data/app_dimensions.dart +++ b/lib/data/app_dimensions.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:flutter/material.dart'; /// Holds the dimensions of the app. @@ -18,13 +20,17 @@ class AppDimensions extends InheritedWidget { super.key, required super.child, required Orientation orientation, - required double screenWidth, + required this.screenWidth, + required this.screenHeight, }) : shouldDisplayPortraitUI = (orientation == Orientation.portrait) && (screenWidth < _portraitModeMaxWidth) { generatorScreenWidth = shouldDisplayPortraitUI ? screenWidth : _landscapeGeneratorScreenWidth; } + final double screenHeight; + final double screenWidth; + final double deleteButtonIconSize = 16; /// Whether the app should display the portrait UI. @@ -60,17 +66,56 @@ class AppDimensions extends InheritedWidget { numberOfCompactButtonPerRow; double get compactButtonHeight => 32; double get compactButtonPadding => 16; + double get wideButtonWidth => generatorScreenContentWidth; double get wideButtonHeight => 48; double get widebuttonPadding => 24; - double get expansionIconSize => 20; + double get expansionIconSize => 16; + double get selectionContainerMainTitleWidth => (generatorScreenContentWidth - (compactButtonWidth + (2 * compactButtonMargin) + expansionIconSize)); + double get bannerAdHorizontalPadding => generatorScreenHorizontalPadding; + + double get toolBarHeight => 48; + + double get chooseRandomGradientIconButtonSize => 16; + + double get sampleTitleBottomMargin => 2.0; + + // Using 6 instead of 16 to match the design due to additional space + // added by the shuffle button in the `ColorAndStopSelectionWidget` + double get sampleSectionVerticalPadding => 6.0; + + double get footerVerticalPadding => 8.0; + + double get samplesListViewSize => + screenHeight - + ((chooseRandomGradientIconButtonSize * 2) + + sampleTitleBottomMargin + + (2 * sampleSectionVerticalPadding) + + (2 * toolBarHeight) + + (2 * footerVerticalPadding) + + (4 * sampleSectionVerticalPadding) + + 16.0 + + (3 * 14.0) + + (3 * 8.0) + + (2 * sampleSectionVerticalPadding)); + + double get _minimumPreviewSectionWidth => generatorScreenWidth; + + double get previewSectionWidth => max( + screenWidth - (2 * generatorScreenWidth), _minimumPreviewSectionWidth); + + double get toolBarIconButtonSize => 20; + + /// Ensure to update this method when adding new properties or constructor + /// parameters that should trigger a rebuild when changed. @override bool updateShouldNotify(covariant AppDimensions oldWidget) { return oldWidget.shouldDisplayPortraitUI != shouldDisplayPortraitUI || - oldWidget.generatorScreenWidth != generatorScreenWidth; + oldWidget.screenWidth != screenWidth || + oldWidget.screenHeight != screenHeight; } static AppDimensions? maybeOf(BuildContext context) { diff --git a/lib/data/app_strings.dart b/lib/data/app_strings.dart index 5d425a0..f863056 100644 --- a/lib/data/app_strings.dart +++ b/lib/data/app_strings.dart @@ -1,29 +1,39 @@ class AppStrings { - static const String appTitle = 'Flutter Gradient Generator'; - static final String appTitleNewLine = appTitle.replaceAll(' ', ' \n'); - static const String copyGradientCode = 'Copy Gradient Code'; - static const String gradientCodeCopied = 'Copied to Clipboard!'; - static const String style = 'Style'; - static const String linear = 'Linear'; - static const String radial = 'Radial'; - static const String colors = 'Colors'; - static const String colorsAndStops = 'Colors & Stops'; - static const String random = 'Random'; - static const String direction = 'Direction'; - static const String victorEronmosele = 'Victor Eronmosele'; - static const String built = 'Built'; - static const String by = 'by'; - static const String tapToEdit = 'Tap to edit'; - static const String enterInPercentage = 'Enter in %'; - static const String sweep = 'Sweep'; - static const String addColor = 'Add Color'; - static const String deleteColor = 'Delete Color'; + static const appTitle = 'Flutter Gradient Generator'; + static const copyGradientCode = 'Copy Gradient Code'; + static const gradientCodeCopied = 'Copied to Clipboard!'; + static const style = 'Style'; + static const linear = 'Linear'; + static const radial = 'Radial'; + static const colors = 'Colors'; + static const colorsAndStops = 'Colors & Stops'; + static const random = 'Random'; + static const direction = 'Direction'; + static const tapToEdit = 'Tap to edit'; + static const enterInPercentage = 'Enter in %'; + static const sweep = 'Sweep'; + static const addColor = 'Add Color'; + static const deleteColor = 'Delete Color'; + static const github = 'GitHub'; + static const builtBy = 'Built by'; + static const victorEronmosele = 'Victor Eronmosele'; + static const shareFeedback = 'Share Feedback'; + static const samples = 'Samples'; + static const chooseRandomGradient = 'Choose Random Gradient'; + static const reportBug = 'Report Bug'; + static const requestFeature = 'Request Feature'; + static const giveFeedback = 'Give Feedback'; + static const viewSourceCodeOnGitHub = 'View Source Code On GitHub'; + static const downloadGradientAsImage = 'Download Gradient As Image'; /// URLs static const githubUrl = 'https://github.com/victoreronmosele/flutter_gradient_generator'; - static const personalWebsiteUrl = 'http://victoreronmosele.com/'; - - /// Firebase Analytics - static const gradientGeneratedFirebaseAnalyticsKey = 'gradientGenerated'; + static const bugReportUrl = + '$githubUrl/issues/new?assignees=&labels=&projects=&template=bug_report.md&title='; + static const featureRequestUrl = + '$githubUrl/issues/new?assignees=&labels=&projects=&template=feature_request.md&title='; + static const feedbackUrl = '$githubUrl/discussions/new?category=general'; + static const victorEronmoseleWebsiteUrl = 'http://victoreronmosele.com/'; + static const appWebsiteUrl = 'https://fluttergradientgenerator.com/'; } diff --git a/lib/data/app_typedefs.dart b/lib/data/app_typedefs.dart index 3a01da6..1889087 100644 --- a/lib/data/app_typedefs.dart +++ b/lib/data/app_typedefs.dart @@ -10,3 +10,8 @@ typedef ColorAndStop = ({ Color color, Stop stop, }); + +/// [FlutterGradientConverter] is a function that converts [colors] and optional +/// [stops] to a [Gradient] from Flutter's painting library. +typedef FlutterGradientConverter = Gradient Function( + {required List colors, List? stops}); diff --git a/lib/generated/gradient_samples.dart b/lib/generated/gradient_samples.dart new file mode 100644 index 0000000..86519b0 --- /dev/null +++ b/lib/generated/gradient_samples.dart @@ -0,0 +1,1604 @@ +// This is a generated file. Do not edit it manually. +// +// To regenerate this file, run the following command from the project root: +// dart tool/bin/generate_gradient_samples.dart + +import 'package:flutter/material.dart'; + +class _GradientSample { + const _GradientSample({required this.name, required this.colors}); + + final String name; + final List colors; +} + +const gradientSamples = <_GradientSample>[ + _GradientSample(name: 'Omolon', colors: [ + Color(0xFF091E3A), + Color(0xFF2F80ED), + Color(0xFF2D9EE0), + ]), + _GradientSample(name: 'Farhan', colors: [ + Color(0xFF9400D3), + Color(0xFF4B0082), + ]), + _GradientSample(name: 'Purple', colors: [ + Color(0xFFc84e89), + Color(0xFFF15F79), + ]), + _GradientSample(name: 'Ibtesam', colors: [ + Color(0xFF00F5A0), + Color(0xFF00D9F5), + ]), + _GradientSample(name: 'Radioactive Heat', colors: [ + Color(0xFFF7941E), + Color(0xFF72C6EF), + Color(0xFF00A651), + ]), + _GradientSample(name: 'The Sky And The Sea', colors: [ + Color(0xFFF7941E), + Color(0xFF004E8F), + ]), + _GradientSample(name: 'From Ice To Fire', colors: [ + Color(0xFF72C6EF), + Color(0xFF004E8F), + ]), + _GradientSample(name: 'Blue & Orange', colors: [ + Color(0xFFFD8112), + Color(0xFF0085CA), + ]), + _GradientSample(name: 'Purple Dream', colors: [ + Color(0xFFbf5ae0), + Color(0xFFa811da), + ]), + _GradientSample(name: 'Blu', colors: [ + Color(0xFF00416A), + Color(0xFFE4E5E6), + ]), + _GradientSample(name: 'Summer Breeze', colors: [ + Color(0xFFfbed96), + Color(0xFFabecd6), + ]), + _GradientSample(name: 'Ver', colors: [ + Color(0xFFFFE000), + Color(0xFF799F0C), + ]), + _GradientSample(name: 'Ver Black', colors: [ + Color(0xFFF7F8F8), + Color(0xFFACBB78), + ]), + _GradientSample(name: 'Combi', colors: [ + Color(0xFF00416A), + Color(0xFF799F0C), + Color(0xFFFFE000), + ]), + _GradientSample(name: 'Anwar', colors: [ + Color(0xFF334d50), + Color(0xFFcbcaa5), + ]), + _GradientSample(name: 'Bluelagoo', colors: [ + Color(0xFF0052D4), + Color(0xFF4364F7), + Color(0xFF6FB1FC), + ]), + _GradientSample(name: 'Lunada', colors: [ + Color(0xFF5433FF), + Color(0xFF20BDFF), + Color(0xFFA5FECB), + ]), + _GradientSample(name: 'Reaqua', colors: [ + Color(0xFF799F0C), + Color(0xFFACBB78), + ]), + _GradientSample(name: 'Mango', colors: [ + Color(0xFFffe259), + Color(0xFFffa751), + ]), + _GradientSample(name: 'Bupe', colors: [ + Color(0xFF00416A), + Color(0xFFE4E5E6), + ]), + _GradientSample(name: 'Rea', colors: [ + Color(0xFFFFE000), + Color(0xFF799F0C), + ]), + _GradientSample(name: 'Windy', colors: [ + Color(0xFFacb6e5), + Color(0xFF86fde8), + ]), + _GradientSample(name: 'Royal Blue', colors: [ + Color(0xFF536976), + Color(0xFF292E49), + ]), + _GradientSample(name: 'Royal Blue + Petrol', colors: [ + Color(0xFFBBD2C5), + Color(0xFF536976), + Color(0xFF292E49), + ]), + _GradientSample(name: 'Copper', colors: [ + Color(0xFFB79891), + Color(0xFF94716B), + ]), + _GradientSample(name: 'Anamnisar', colors: [ + Color(0xFF9796f0), + Color(0xFFfbc7d4), + ]), + _GradientSample(name: 'Petrol', colors: [ + Color(0xFFBBD2C5), + Color(0xFF536976), + ]), + _GradientSample(name: 'Sky', colors: [ + Color(0xFF076585), + Color(0xFFffffff), + ]), + _GradientSample(name: 'Sel', colors: [ + Color(0xFF00467F), + Color(0xFFA5CC82), + ]), + _GradientSample(name: 'Afternoon', colors: [ + Color(0xFF000C40), + Color(0xFF607D8B), + ]), + _GradientSample(name: 'Skyline', colors: [ + Color(0xFF1488CC), + Color(0xFF2B32B2), + ]), + _GradientSample(name: 'DIMIGO', colors: [ + Color(0xFFec008c), + Color(0xFFfc6767), + ]), + _GradientSample(name: 'Purple Love', colors: [ + Color(0xFFcc2b5e), + Color(0xFF753a88), + ]), + _GradientSample(name: 'Sexy Blue', colors: [ + Color(0xFF2193b0), + Color(0xFF6dd5ed), + ]), + _GradientSample(name: 'Blooker20', colors: [ + Color(0xFFe65c00), + Color(0xFFF9D423), + ]), + _GradientSample(name: 'Sea Blue', colors: [ + Color(0xFF2b5876), + Color(0xFF4e4376), + ]), + _GradientSample(name: 'Nimvelo', colors: [ + Color(0xFF314755), + Color(0xFF26a0da), + ]), + _GradientSample(name: 'Hazel', colors: [ + Color(0xFF77A1D3), + Color(0xFF79CBCA), + Color(0xFFE684AE), + ]), + _GradientSample(name: 'Noon to Dusk', colors: [ + Color(0xFFff6e7f), + Color(0xFFbfe9ff), + ]), + _GradientSample(name: 'YouTube', colors: [ + Color(0xFFe52d27), + Color(0xFFb31217), + ]), + _GradientSample(name: 'Cool Brown', colors: [ + Color(0xFF603813), + Color(0xFFb29f94), + ]), + _GradientSample(name: 'Harmonic Energy', colors: [ + Color(0xFF16A085), + Color(0xFFF4D03F), + ]), + _GradientSample(name: 'Playing with Reds', colors: [ + Color(0xFFD31027), + Color(0xFFEA384D), + ]), + _GradientSample(name: 'Sunny Days', colors: [ + Color(0xFFEDE574), + Color(0xFFE1F5C4), + ]), + _GradientSample(name: 'Green Beach', colors: [ + Color(0xFF02AAB0), + Color(0xFF00CDAC), + ]), + _GradientSample(name: 'Intuitive Purple', colors: [ + Color(0xFFDA22FF), + Color(0xFF9733EE), + ]), + _GradientSample(name: 'Emerald Water', colors: [ + Color(0xFF348F50), + Color(0xFF56B4D3), + ]), + _GradientSample(name: 'Lemon Twist', colors: [ + Color(0xFF3CA55C), + Color(0xFFB5AC49), + ]), + _GradientSample(name: 'Monte Carlo', colors: [ + Color(0xFFCC95C0), + Color(0xFFDBD4B4), + Color(0xFF7AA1D2), + ]), + _GradientSample(name: 'Horizon', colors: [ + Color(0xFF003973), + Color(0xFFE5E5BE), + ]), + _GradientSample(name: 'Rose Water', colors: [ + Color(0xFFE55D87), + Color(0xFF5FC3E4), + ]), + _GradientSample(name: 'Frozen', colors: [ + Color(0xFF403B4A), + Color(0xFFE7E9BB), + ]), + _GradientSample(name: 'Mango Pulp', colors: [ + Color(0xFFF09819), + Color(0xFFEDDE5D), + ]), + _GradientSample(name: 'Bloody Mary', colors: [ + Color(0xFFFF512F), + Color(0xFFDD2476), + ]), + _GradientSample(name: 'Aubergine', colors: [ + Color(0xFFAA076B), + Color(0xFF61045F), + ]), + _GradientSample(name: 'Aqua Marine', colors: [ + Color(0xFF1A2980), + Color(0xFF26D0CE), + ]), + _GradientSample(name: 'Sunrise', colors: [ + Color(0xFFFF512F), + Color(0xFFF09819), + ]), + _GradientSample(name: 'Purple Paradise', colors: [ + Color(0xFF1D2B64), + Color(0xFFF8CDDA), + ]), + _GradientSample(name: 'Stripe', colors: [ + Color(0xFF1FA2FF), + Color(0xFF12D8FA), + Color(0xFFA6FFCB), + ]), + _GradientSample(name: 'Sea Weed', colors: [ + Color(0xFF4CB8C4), + Color(0xFF3CD3AD), + ]), + _GradientSample(name: 'Pinky', colors: [ + Color(0xFFDD5E89), + Color(0xFFF7BB97), + ]), + _GradientSample(name: 'Cherry', colors: [ + Color(0xFFEB3349), + Color(0xFFF45C43), + ]), + _GradientSample(name: 'Mojito', colors: [ + Color(0xFF1D976C), + Color(0xFF93F9B9), + ]), + _GradientSample(name: 'Juicy Orange', colors: [ + Color(0xFFFF8008), + Color(0xFFFFC837), + ]), + _GradientSample(name: 'Mirage', colors: [ + Color(0xFF16222A), + Color(0xFF3A6073), + ]), + _GradientSample(name: 'Steel Gray', colors: [ + Color(0xFF1F1C2C), + Color(0xFF928DAB), + ]), + _GradientSample(name: 'Kashmir', colors: [ + Color(0xFF614385), + Color(0xFF516395), + ]), + _GradientSample(name: 'Electric Violet', colors: [ + Color(0xFF4776E6), + Color(0xFF8E54E9), + ]), + _GradientSample(name: 'Venice Blue', colors: [ + Color(0xFF085078), + Color(0xFF85D8CE), + ]), + _GradientSample(name: 'Bora Bora', colors: [ + Color(0xFF2BC0E4), + Color(0xFFEAECC6), + ]), + _GradientSample(name: 'Moss', colors: [ + Color(0xFF134E5E), + Color(0xFF71B280), + ]), + _GradientSample(name: 'Shroom Haze', colors: [ + Color(0xFF5C258D), + Color(0xFF4389A2), + ]), + _GradientSample(name: 'Mystic', colors: [ + Color(0xFF757F9A), + Color(0xFFD7DDE8), + ]), + _GradientSample(name: 'Midnight City', colors: [ + Color(0xFF232526), + Color(0xFF414345), + ]), + _GradientSample(name: 'Sea Blizz', colors: [ + Color(0xFF1CD8D2), + Color(0xFF93EDC7), + ]), + _GradientSample(name: 'Opa', colors: [ + Color(0xFF3D7EAA), + Color(0xFFFFE47A), + ]), + _GradientSample(name: 'Titanium', colors: [ + Color(0xFF283048), + Color(0xFF859398), + ]), + _GradientSample(name: 'Mantle', colors: [ + Color(0xFF24C6DC), + Color(0xFF514A9D), + ]), + _GradientSample(name: 'Dracula', colors: [ + Color(0xFFDC2424), + Color(0xFF4A569D), + ]), + _GradientSample(name: 'Peach', colors: [ + Color(0xFFED4264), + Color(0xFFFFEDBC), + ]), + _GradientSample(name: 'Moonrise', colors: [ + Color(0xFFDAE2F8), + Color(0xFFD6A4A4), + ]), + _GradientSample(name: 'Clouds', colors: [ + Color(0xFFECE9E6), + Color(0xFFFFFFFF), + ]), + _GradientSample(name: 'Stellar', colors: [ + Color(0xFF7474BF), + Color(0xFF348AC7), + ]), + _GradientSample(name: 'Bourbon', colors: [ + Color(0xFFEC6F66), + Color(0xFFF3A183), + ]), + _GradientSample(name: 'Calm Darya', colors: [ + Color(0xFF5f2c82), + Color(0xFF49a09d), + ]), + _GradientSample(name: 'Influenza', colors: [ + Color(0xFFC04848), + Color(0xFF480048), + ]), + _GradientSample(name: 'Shrimpy', colors: [ + Color(0xFFe43a15), + Color(0xFFe65245), + ]), + _GradientSample(name: 'Army', colors: [ + Color(0xFF414d0b), + Color(0xFF727a17), + ]), + _GradientSample(name: 'Miaka', colors: [ + Color(0xFFFC354C), + Color(0xFF0ABFBC), + ]), + _GradientSample(name: 'Pinot Noir', colors: [ + Color(0xFF4b6cb7), + Color(0xFF182848), + ]), + _GradientSample(name: 'Day Tripper', colors: [ + Color(0xFFf857a6), + Color(0xFFff5858), + ]), + _GradientSample(name: 'Namn', colors: [ + Color(0xFFa73737), + Color(0xFF7a2828), + ]), + _GradientSample(name: 'Blurry Beach', colors: [ + Color(0xFFd53369), + Color(0xFFcbad6d), + ]), + _GradientSample(name: 'Vasily', colors: [ + Color(0xFFe9d362), + Color(0xFF333333), + ]), + _GradientSample(name: 'A Lost Memory', colors: [ + Color(0xFFDE6262), + Color(0xFFFFB88C), + ]), + _GradientSample(name: 'Petrichor', colors: [ + Color(0xFF666600), + Color(0xFF999966), + ]), + _GradientSample(name: 'Jonquil', colors: [ + Color(0xFFFFEEEE), + Color(0xFFDDEFBB), + ]), + _GradientSample(name: 'Sirius Tamed', colors: [ + Color(0xFFEFEFBB), + Color(0xFFD4D3DD), + ]), + _GradientSample(name: 'Kyoto', colors: [ + Color(0xFFc21500), + Color(0xFFffc500), + ]), + _GradientSample(name: 'Misty Meadow', colors: [ + Color(0xFF215f00), + Color(0xFFe4e4d9), + ]), + _GradientSample(name: 'Aqualicious', colors: [ + Color(0xFF50C9C3), + Color(0xFF96DEDA), + ]), + _GradientSample(name: 'Moor', colors: [ + Color(0xFF616161), + Color(0xFF9bc5c3), + ]), + _GradientSample(name: 'Almost', colors: [ + Color(0xFFddd6f3), + Color(0xFFfaaca8), + ]), + _GradientSample(name: 'Forever Lost', colors: [ + Color(0xFF5D4157), + Color(0xFFA8CABA), + ]), + _GradientSample(name: 'Winter', colors: [ + Color(0xFFE6DADA), + Color(0xFF274046), + ]), + _GradientSample(name: 'Nelson', colors: [ + Color(0xFFf2709c), + Color(0xFFff9472), + ]), + _GradientSample(name: 'Autumn', colors: [ + Color(0xFFDAD299), + Color(0xFFB0DAB9), + ]), + _GradientSample(name: 'Candy', colors: [ + Color(0xFFD3959B), + Color(0xFFBFE6BA), + ]), + _GradientSample(name: 'Reef', colors: [ + Color(0xFF00d2ff), + Color(0xFF3a7bd5), + ]), + _GradientSample(name: 'The Strain', colors: [ + Color(0xFF870000), + Color(0xFF190A05), + ]), + _GradientSample(name: 'Dirty Fog', colors: [ + Color(0xFFB993D6), + Color(0xFF8CA6DB), + ]), + _GradientSample(name: 'Earthly', colors: [ + Color(0xFF649173), + Color(0xFFDBD5A4), + ]), + _GradientSample(name: 'Virgin', colors: [ + Color(0xFFC9FFBF), + Color(0xFFFFAFBD), + ]), + _GradientSample(name: 'Ash', colors: [ + Color(0xFF606c88), + Color(0xFF3f4c6b), + ]), + _GradientSample(name: 'Cherryblossoms', colors: [ + Color(0xFFFBD3E9), + Color(0xFFBB377D), + ]), + _GradientSample(name: 'Parklife', colors: [ + Color(0xFFADD100), + Color(0xFF7B920A), + ]), + _GradientSample(name: 'Dance To Forget', colors: [ + Color(0xFFFF4E50), + Color(0xFFF9D423), + ]), + _GradientSample(name: 'Starfall', colors: [ + Color(0xFFF0C27B), + Color(0xFF4B1248), + ]), + _GradientSample(name: 'Red Mist', colors: [ + Color(0xFF000000), + Color(0xFFe74c3c), + ]), + _GradientSample(name: 'Teal Love', colors: [ + Color(0xFFAAFFA9), + Color(0xFF11FFBD), + ]), + _GradientSample(name: 'Neon Life', colors: [ + Color(0xFFB3FFAB), + Color(0xFF12FFF7), + ]), + _GradientSample(name: 'Man of Steel', colors: [ + Color(0xFF780206), + Color(0xFF061161), + ]), + _GradientSample(name: 'Amethyst', colors: [ + Color(0xFF9D50BB), + Color(0xFF6E48AA), + ]), + _GradientSample(name: 'Cheer Up Emo Kid', colors: [ + Color(0xFF556270), + Color(0xFFFF6B6B), + ]), + _GradientSample(name: 'Shore', colors: [ + Color(0xFF70e1f5), + Color(0xFFffd194), + ]), + _GradientSample(name: 'Facebook Messenger', colors: [ + Color(0xFF00c6ff), + Color(0xFF0072ff), + ]), + _GradientSample(name: 'SoundCloud', colors: [ + Color(0xFFfe8c00), + Color(0xFFf83600), + ]), + _GradientSample(name: 'Behongo', colors: [ + Color(0xFF52c234), + Color(0xFF061700), + ]), + _GradientSample(name: 'ServQuick', colors: [ + Color(0xFF485563), + Color(0xFF29323c), + ]), + _GradientSample(name: 'Friday', colors: [ + Color(0xFF83a4d4), + Color(0xFFb6fbff), + ]), + _GradientSample(name: 'Martini', colors: [ + Color(0xFFFDFC47), + Color(0xFF24FE41), + ]), + _GradientSample(name: 'Metallic Toad', colors: [ + Color(0xFFabbaab), + Color(0xFFffffff), + ]), + _GradientSample(name: 'Between The Clouds', colors: [ + Color(0xFF73C8A9), + Color(0xFF373B44), + ]), + _GradientSample(name: 'Crazy Orange I', colors: [ + Color(0xFFD38312), + Color(0xFFA83279), + ]), + _GradientSample(name: 'Hersheys', colors: [ + Color(0xFF1e130c), + Color(0xFF9a8478), + ]), + _GradientSample(name: 'Talking To Mice Elf', colors: [ + Color(0xFF948E99), + Color(0xFF2E1437), + ]), + _GradientSample(name: 'Purple Bliss', colors: [ + Color(0xFF360033), + Color(0xFF0b8793), + ]), + _GradientSample(name: 'Predawn', colors: [ + Color(0xFFFFA17F), + Color(0xFF00223E), + ]), + _GradientSample(name: 'Endless River', colors: [ + Color(0xFF43cea2), + Color(0xFF185a9d), + ]), + _GradientSample(name: 'Pastel Orange at the Sun', colors: [ + Color(0xFFffb347), + Color(0xFFffcc33), + ]), + _GradientSample(name: 'Twitch', colors: [ + Color(0xFF6441A5), + Color(0xFF2a0845), + ]), + _GradientSample(name: 'Atlas', colors: [ + Color(0xFFFEAC5E), + Color(0xFFC779D0), + Color(0xFF4BC0C8), + ]), + _GradientSample(name: 'Instagram', colors: [ + Color(0xFF833ab4), + Color(0xFFfd1d1d), + Color(0xFFfcb045), + ]), + _GradientSample(name: 'Flickr', colors: [ + Color(0xFFff0084), + Color(0xFF33001b), + ]), + _GradientSample(name: 'Vine', colors: [ + Color(0xFF00bf8f), + Color(0xFF001510), + ]), + _GradientSample(name: 'Turquoise flow', colors: [ + Color(0xFF136a8a), + Color(0xFF267871), + ]), + _GradientSample(name: 'Portrait', colors: [ + Color(0xFF8e9eab), + Color(0xFFeef2f3), + ]), + _GradientSample(name: 'Virgin America', colors: [ + Color(0xFF7b4397), + Color(0xFFdc2430), + ]), + _GradientSample(name: 'Koko Caramel', colors: [ + Color(0xFFD1913C), + Color(0xFFFFD194), + ]), + _GradientSample(name: 'Fresh Turboscent', colors: [ + Color(0xFFF1F2B5), + Color(0xFF135058), + ]), + _GradientSample(name: 'Green to dark', colors: [ + Color(0xFF6A9113), + Color(0xFF141517), + ]), + _GradientSample(name: 'Ukraine', colors: [ + Color(0xFF004FF9), + Color(0xFFFFF94C), + ]), + _GradientSample(name: 'Curiosity blue', colors: [ + Color(0xFF525252), + Color(0xFF3d72b4), + ]), + _GradientSample(name: 'Dark Knight', colors: [ + Color(0xFFBA8B02), + Color(0xFF181818), + ]), + _GradientSample(name: 'Piglet', colors: [ + Color(0xFFee9ca7), + Color(0xFFffdde1), + ]), + _GradientSample(name: 'Lizard', colors: [ + Color(0xFF304352), + Color(0xFFd7d2cc), + ]), + _GradientSample(name: 'Sage Persuasion', colors: [ + Color(0xFFCCCCB2), + Color(0xFF757519), + ]), + _GradientSample(name: 'Between Night and Day', colors: [ + Color(0xFF2c3e50), + Color(0xFF3498db), + ]), + _GradientSample(name: 'Timber', colors: [ + Color(0xFFfc00ff), + Color(0xFF00dbde), + ]), + _GradientSample(name: 'Passion', colors: [ + Color(0xFFe53935), + Color(0xFFe35d5b), + ]), + _GradientSample(name: 'Clear Sky', colors: [ + Color(0xFF005C97), + Color(0xFF363795), + ]), + _GradientSample(name: 'Master Card', colors: [ + Color(0xFFf46b45), + Color(0xFFeea849), + ]), + _GradientSample(name: 'Back To Earth', colors: [ + Color(0xFF00C9FF), + Color(0xFF92FE9D), + ]), + _GradientSample(name: 'Deep Purple', colors: [ + Color(0xFF673AB7), + Color(0xFF512DA8), + ]), + _GradientSample(name: 'Little Leaf', colors: [ + Color(0xFF76b852), + Color(0xFF8DC26F), + ]), + _GradientSample(name: 'Netflix', colors: [ + Color(0xFF8E0E00), + Color(0xFF1F1C18), + ]), + _GradientSample(name: 'Light Orange', colors: [ + Color(0xFFFFB75E), + Color(0xFFED8F03), + ]), + _GradientSample(name: 'Green and Blue', colors: [ + Color(0xFFc2e59c), + Color(0xFF64b3f4), + ]), + _GradientSample(name: 'Poncho', colors: [ + Color(0xFF403A3E), + Color(0xFFBE5869), + ]), + _GradientSample(name: 'Back to the Future', colors: [ + Color(0xFFC02425), + Color(0xFFF0CB35), + ]), + _GradientSample(name: 'Blush', colors: [ + Color(0xFFB24592), + Color(0xFFF15F79), + ]), + _GradientSample(name: 'Inbox', colors: [ + Color(0xFF457fca), + Color(0xFF5691c8), + ]), + _GradientSample(name: 'Purplin', colors: [ + Color(0xFF6a3093), + Color(0xFFa044ff), + ]), + _GradientSample(name: 'Pale Wood', colors: [ + Color(0xFFeacda3), + Color(0xFFd6ae7b), + ]), + _GradientSample(name: 'Haikus', colors: [ + Color(0xFFfd746c), + Color(0xFFff9068), + ]), + _GradientSample(name: 'Pizelex', colors: [ + Color(0xFF114357), + Color(0xFFF29492), + ]), + _GradientSample(name: 'Joomla', colors: [ + Color(0xFF1e3c72), + Color(0xFF2a5298), + ]), + _GradientSample(name: 'Christmas', colors: [ + Color(0xFF2F7336), + Color(0xFFAA3A38), + ]), + _GradientSample(name: 'Minnesota Vikings', colors: [ + Color(0xFF5614B0), + Color(0xFFDBD65C), + ]), + _GradientSample(name: 'Miami Dolphins', colors: [ + Color(0xFF4DA0B0), + Color(0xFFD39D38), + ]), + _GradientSample(name: 'Forest', colors: [ + Color(0xFF5A3F37), + Color(0xFF2C7744), + ]), + _GradientSample(name: 'Nighthawk', colors: [ + Color(0xFF2980b9), + Color(0xFF2c3e50), + ]), + _GradientSample(name: 'Superman', colors: [ + Color(0xFF0099F7), + Color(0xFFF11712), + ]), + _GradientSample(name: 'Suzy', colors: [ + Color(0xFF834d9b), + Color(0xFFd04ed6), + ]), + _GradientSample(name: 'Dark Skies', colors: [ + Color(0xFF4B79A1), + Color(0xFF283E51), + ]), + _GradientSample(name: 'Deep Space', colors: [ + Color(0xFF000000), + Color(0xFF434343), + ]), + _GradientSample(name: 'Decent', colors: [ + Color(0xFF4CA1AF), + Color(0xFFC4E0E5), + ]), + _GradientSample(name: 'Colors Of Sky', colors: [ + Color(0xFFE0EAFC), + Color(0xFFCFDEF3), + ]), + _GradientSample(name: 'Purple White', colors: [ + Color(0xFFBA5370), + Color(0xFFF4E2D8), + ]), + _GradientSample(name: 'Ali', colors: [ + Color(0xFFff4b1f), + Color(0xFF1fddff), + ]), + _GradientSample(name: 'Alihossein', colors: [ + Color(0xFFf7ff00), + Color(0xFFdb36a4), + ]), + _GradientSample(name: 'Shahabi', colors: [ + Color(0xFFa80077), + Color(0xFF66ff00), + ]), + _GradientSample(name: 'Red Ocean', colors: [ + Color(0xFF1D4350), + Color(0xFFA43931), + ]), + _GradientSample(name: 'Tranquil', colors: [ + Color(0xFFEECDA3), + Color(0xFFEF629F), + ]), + _GradientSample(name: 'Transfile', colors: [ + Color(0xFF16BFFD), + Color(0xFFCB3066), + ]), + _GradientSample(name: 'Sylvia', colors: [ + Color(0xFFff4b1f), + Color(0xFFff9068), + ]), + _GradientSample(name: 'Sweet Morning', colors: [ + Color(0xFFFF5F6D), + Color(0xFFFFC371), + ]), + _GradientSample(name: 'Politics', colors: [ + Color(0xFF2196f3), + Color(0xFFf44336), + ]), + _GradientSample(name: 'Bright Vault', colors: [ + Color(0xFF00d2ff), + Color(0xFF928DAB), + ]), + _GradientSample(name: 'Solid Vault', colors: [ + Color(0xFF3a7bd5), + Color(0xFF3a6073), + ]), + _GradientSample(name: 'Sunset', colors: [ + Color(0xFF0B486B), + Color(0xFFF56217), + ]), + _GradientSample(name: 'Grapefruit Sunset', colors: [ + Color(0xFFe96443), + Color(0xFF904e95), + ]), + _GradientSample(name: 'Deep Sea Space', colors: [ + Color(0xFF2C3E50), + Color(0xFF4CA1AF), + ]), + _GradientSample(name: 'Dusk', colors: [ + Color(0xFF2C3E50), + Color(0xFFFD746C), + ]), + _GradientSample(name: 'Minimal Red', colors: [ + Color(0xFFF00000), + Color(0xFFDC281E), + ]), + _GradientSample(name: 'Royal', colors: [ + Color(0xFF141E30), + Color(0xFF243B55), + ]), + _GradientSample(name: 'Mauve', colors: [ + Color(0xFF42275a), + Color(0xFF734b6d), + ]), + _GradientSample(name: 'Frost', colors: [ + Color(0xFF000428), + Color(0xFF004e92), + ]), + _GradientSample(name: 'Lush', colors: [ + Color(0xFF56ab2f), + Color(0xFFa8e063), + ]), + _GradientSample(name: 'Firewatch', colors: [ + Color(0xFFcb2d3e), + Color(0xFFef473a), + ]), + _GradientSample(name: 'Sherbert', colors: [ + Color(0xFFf79d00), + Color(0xFF64f38c), + ]), + _GradientSample(name: 'Blood Red', colors: [ + Color(0xFFf85032), + Color(0xFFe73827), + ]), + _GradientSample(name: 'Sun on the Horizon', colors: [ + Color(0xFFfceabb), + Color(0xFFf8b500), + ]), + _GradientSample(name: 'IIIT Delhi', colors: [ + Color(0xFF808080), + Color(0xFF3fada8), + ]), + _GradientSample(name: 'Jupiter', colors: [ + Color(0xFFffd89b), + Color(0xFF19547b), + ]), + _GradientSample(name: '50 Shades of Grey', colors: [ + Color(0xFFbdc3c7), + Color(0xFF2c3e50), + ]), + _GradientSample(name: 'Dania', colors: [ + Color(0xFFBE93C5), + Color(0xFF7BC6CC), + ]), + _GradientSample(name: 'Limeade', colors: [ + Color(0xFFA1FFCE), + Color(0xFFFAFFD1), + ]), + _GradientSample(name: 'Disco', colors: [ + Color(0xFF4ECDC4), + Color(0xFF556270), + ]), + _GradientSample(name: 'Love Couple', colors: [ + Color(0xFF3a6186), + Color(0xFF89253e), + ]), + _GradientSample(name: 'Azure Pop', colors: [ + Color(0xFFef32d9), + Color(0xFF89fffd), + ]), + _GradientSample(name: 'Nepal', colors: [ + Color(0xFFde6161), + Color(0xFF2657eb), + ]), + _GradientSample(name: 'Cosmic Fusion', colors: [ + Color(0xFFff00cc), + Color(0xFF333399), + ]), + _GradientSample(name: 'Snapchat', colors: [ + Color(0xFFfffc00), + Color(0xFFffffff), + ]), + _GradientSample(name: 'Ed\'s Sunset Gradient', colors: [ + Color(0xFFff7e5f), + Color(0xFFfeb47b), + ]), + _GradientSample(name: 'Brady Brady Fun Fun', colors: [ + Color(0xFF00c3ff), + Color(0xFFffff1c), + ]), + _GradientSample(name: 'Black Rosé', colors: [ + Color(0xFFf4c4f3), + Color(0xFFfc67fa), + ]), + _GradientSample(name: '80\'s Purple', colors: [ + Color(0xFF41295a), + Color(0xFF2F0743), + ]), + _GradientSample(name: 'Radar', colors: [ + Color(0xFFA770EF), + Color(0xFFCF8BF3), + Color(0xFFFDB99B), + ]), + _GradientSample(name: 'Ibiza Sunset', colors: [ + Color(0xFFee0979), + Color(0xFFff6a00), + ]), + _GradientSample(name: 'Dawn', colors: [ + Color(0xFFF3904F), + Color(0xFF3B4371), + ]), + _GradientSample(name: 'Mild', colors: [ + Color(0xFF67B26F), + Color(0xFF4ca2cd), + ]), + _GradientSample(name: 'Vice City', colors: [ + Color(0xFF3494E6), + Color(0xFFEC6EAD), + ]), + _GradientSample(name: 'Jaipur', colors: [ + Color(0xFFDBE6F6), + Color(0xFFC5796D), + ]), + _GradientSample(name: 'Jodhpur', colors: [ + Color(0xFF9CECFB), + Color(0xFF65C7F7), + Color(0xFF0052D4), + ]), + _GradientSample(name: 'Cocoaa Ice', colors: [ + Color(0xFFc0c0aa), + Color(0xFF1cefff), + ]), + _GradientSample(name: 'EasyMed', colors: [ + Color(0xFFDCE35B), + Color(0xFF45B649), + ]), + _GradientSample(name: 'Rose Colored Lenses', colors: [ + Color(0xFFE8CBC0), + Color(0xFF636FA4), + ]), + _GradientSample(name: 'What lies Beyond', colors: [ + Color(0xFFF0F2F0), + Color(0xFF000C40), + ]), + _GradientSample(name: 'Roseanna', colors: [ + Color(0xFFFFAFBD), + Color(0xFFffc3a0), + ]), + _GradientSample(name: 'Honey Dew', colors: [ + Color(0xFF43C6AC), + Color(0xFFF8FFAE), + ]), + _GradientSample(name: 'Under the Lake', colors: [ + Color(0xFF093028), + Color(0xFF237A57), + ]), + _GradientSample(name: 'The Blue Lagoon', colors: [ + Color(0xFF43C6AC), + Color(0xFF191654), + ]), + _GradientSample(name: 'Can You Feel The Love Tonight', colors: [ + Color(0xFF4568DC), + Color(0xFFB06AB3), + ]), + _GradientSample(name: 'Very Blue', colors: [ + Color(0xFF0575E6), + Color(0xFF021B79), + ]), + _GradientSample(name: 'Love and Liberty', colors: [ + Color(0xFF200122), + Color(0xFF6f0000), + ]), + _GradientSample(name: 'Orca', colors: [ + Color(0xFF44A08D), + Color(0xFF093637), + ]), + _GradientSample(name: 'Venice', colors: [ + Color(0xFF6190E8), + Color(0xFFA7BFE8), + ]), + _GradientSample(name: 'Pacific Dream', colors: [ + Color(0xFF34e89e), + Color(0xFF0f3443), + ]), + _GradientSample(name: 'Learning and Leading', colors: [ + Color(0xFFF7971E), + Color(0xFFFFD200), + ]), + _GradientSample(name: 'Celestial', colors: [ + Color(0xFFC33764), + Color(0xFF1D2671), + ]), + _GradientSample(name: 'Purplepine', colors: [ + Color(0xFF20002c), + Color(0xFFcbb4d4), + ]), + _GradientSample(name: 'Sha la la', colors: [ + Color(0xFFD66D75), + Color(0xFFE29587), + ]), + _GradientSample(name: 'Mini', colors: [ + Color(0xFF30E8BF), + Color(0xFFFF8235), + ]), + _GradientSample(name: 'Maldives', colors: [ + Color(0xFFB2FEFA), + Color(0xFF0ED2F7), + ]), + _GradientSample(name: 'Cinnamint', colors: [ + Color(0xFF4AC29A), + Color(0xFFBDFFF3), + ]), + _GradientSample(name: 'Html', colors: [ + Color(0xFFE44D26), + Color(0xFFF16529), + ]), + _GradientSample(name: 'Coal', colors: [ + Color(0xFFEB5757), + Color(0xFF000000), + ]), + _GradientSample(name: 'Sunkist', colors: [ + Color(0xFFF2994A), + Color(0xFFF2C94C), + ]), + _GradientSample(name: 'Blue Skies', colors: [ + Color(0xFF56CCF2), + Color(0xFF2F80ED), + ]), + _GradientSample(name: 'Chitty Chitty Bang Bang', colors: [ + Color(0xFF007991), + Color(0xFF78ffd6), + ]), + _GradientSample(name: 'Visions of Grandeur', colors: [ + Color(0xFF000046), + Color(0xFF1CB5E0), + ]), + _GradientSample(name: 'Crystal Clear', colors: [ + Color(0xFF159957), + Color(0xFF155799), + ]), + _GradientSample(name: 'Mello', colors: [ + Color(0xFFc0392b), + Color(0xFF8e44ad), + ]), + _GradientSample(name: 'Compare Now', colors: [ + Color(0xFFEF3B36), + Color(0xFFFFFFFF), + ]), + _GradientSample(name: 'Meridian', colors: [ + Color(0xFF283c86), + Color(0xFF45a247), + ]), + _GradientSample(name: 'Relay', colors: [ + Color(0xFF3A1C71), + Color(0xFFD76D77), + Color(0xFFFFAF7B), + ]), + _GradientSample(name: 'Alive', colors: [ + Color(0xFFCB356B), + Color(0xFFBD3F32), + ]), + _GradientSample(name: 'Scooter', colors: [ + Color(0xFF36D1DC), + Color(0xFF5B86E5), + ]), + _GradientSample(name: 'Terminal', colors: [ + Color(0xFF000000), + Color(0xFF0f9b0f), + ]), + _GradientSample(name: 'Telegram', colors: [ + Color(0xFF1c92d2), + Color(0xFFf2fcfe), + ]), + _GradientSample(name: 'Crimson Tide', colors: [ + Color(0xFF642B73), + Color(0xFFC6426E), + ]), + _GradientSample(name: 'Socialive', colors: [ + Color(0xFF06beb6), + Color(0xFF48b1bf), + ]), + _GradientSample(name: 'Subu', colors: [ + Color(0xFF0cebeb), + Color(0xFF20e3b2), + Color(0xFF29ffc6), + ]), + _GradientSample(name: 'Broken Hearts', colors: [ + Color(0xFFd9a7c7), + Color(0xFFfffcdc), + ]), + _GradientSample(name: 'Kimoby Is The New Blue', colors: [ + Color(0xFF396afc), + Color(0xFF2948ff), + ]), + _GradientSample(name: 'Dull', colors: [ + Color(0xFFC9D6FF), + Color(0xFFE2E2E2), + ]), + _GradientSample(name: 'Purpink', colors: [ + Color(0xFF7F00FF), + Color(0xFFE100FF), + ]), + _GradientSample(name: 'Orange Coral', colors: [ + Color(0xFFff9966), + Color(0xFFff5e62), + ]), + _GradientSample(name: 'Summer', colors: [ + Color(0xFF22c1c3), + Color(0xFFfdbb2d), + ]), + _GradientSample(name: 'King Yna', colors: [ + Color(0xFF1a2a6c), + Color(0xFFb21f1f), + Color(0xFFfdbb2d), + ]), + _GradientSample(name: 'Velvet Sun', colors: [ + Color(0xFFe1eec3), + Color(0xFFf05053), + ]), + _GradientSample(name: 'Zinc', colors: [ + Color(0xFFADA996), + Color(0xFFF2F2F2), + Color(0xFFDBDBDB), + Color(0xFFEAEAEA), + ]), + _GradientSample(name: 'Hydrogen', colors: [ + Color(0xFF667db6), + Color(0xFF0082c8), + Color(0xFF0082c8), + Color(0xFF667db6), + ]), + _GradientSample(name: 'Argon', colors: [ + Color(0xFF03001e), + Color(0xFF7303c0), + Color(0xFFec38bc), + Color(0xFFfdeff9), + ]), + _GradientSample(name: 'Lithium', colors: [ + Color(0xFF6D6027), + Color(0xFFD3CBB8), + ]), + _GradientSample(name: 'Digital Water', colors: [ + Color(0xFF74ebd5), + Color(0xFFACB6E5), + ]), + _GradientSample(name: 'Orange Fun', colors: [ + Color(0xFFfc4a1a), + Color(0xFFf7b733), + ]), + _GradientSample(name: 'Rainbow Blue', colors: [ + Color(0xFF00F260), + Color(0xFF0575E6), + ]), + _GradientSample(name: 'Pink Flavour', colors: [ + Color(0xFF800080), + Color(0xFFffc0cb), + ]), + _GradientSample(name: 'Sulphur', colors: [ + Color(0xFFCAC531), + Color(0xFFF3F9A7), + ]), + _GradientSample(name: 'Selenium', colors: [ + Color(0xFF3C3B3F), + Color(0xFF605C3C), + ]), + _GradientSample(name: 'Delicate', colors: [ + Color(0xFFD3CCE3), + Color(0xFFE9E4F0), + ]), + _GradientSample(name: 'Ohhappiness', colors: [ + Color(0xFF00b09b), + Color(0xFF96c93d), + ]), + _GradientSample(name: 'Lawrencium', colors: [ + Color(0xFF0f0c29), + Color(0xFF302b63), + Color(0xFF24243e), + ]), + _GradientSample(name: 'Relaxing red', colors: [ + Color(0xFFfffbd5), + Color(0xFFb20a2c), + ]), + _GradientSample(name: 'Taran Tado', colors: [ + Color(0xFF23074d), + Color(0xFFcc5333), + ]), + _GradientSample(name: 'Bighead', colors: [ + Color(0xFFc94b4b), + Color(0xFF4b134f), + ]), + _GradientSample(name: 'Sublime Vivid', colors: [ + Color(0xFFFC466B), + Color(0xFF3F5EFB), + ]), + _GradientSample(name: 'Sublime Light', colors: [ + Color(0xFFFC5C7D), + Color(0xFF6A82FB), + ]), + _GradientSample(name: 'Pun Yeta', colors: [ + Color(0xFF108dc7), + Color(0xFFef8e38), + ]), + _GradientSample(name: 'Quepal', colors: [ + Color(0xFF11998e), + Color(0xFF38ef7d), + ]), + _GradientSample(name: 'Sand to Blue', colors: [ + Color(0xFF3E5151), + Color(0xFFDECBA4), + ]), + _GradientSample(name: 'Wedding Day Blues', colors: [ + Color(0xFF40E0D0), + Color(0xFFFF8C00), + Color(0xFFFF0080), + ]), + _GradientSample(name: 'Shifter', colors: [ + Color(0xFFbc4e9c), + Color(0xFFf80759), + ]), + _GradientSample(name: 'Red Sunset', colors: [ + Color(0xFF355C7D), + Color(0xFF6C5B7B), + Color(0xFFC06C84), + ]), + _GradientSample(name: 'Moon Purple', colors: [ + Color(0xFF4e54c8), + Color(0xFF8f94fb), + ]), + _GradientSample(name: 'Pure Lust', colors: [ + Color(0xFF333333), + Color(0xFFdd1818), + ]), + _GradientSample(name: 'Slight Ocean View', colors: [ + Color(0xFFa8c0ff), + Color(0xFF3f2b96), + ]), + _GradientSample(name: 'eXpresso', colors: [ + Color(0xFFad5389), + Color(0xFF3c1053), + ]), + _GradientSample(name: 'Shifty', colors: [ + Color(0xFF636363), + Color(0xFFa2ab58), + ]), + _GradientSample(name: 'Vanusa', colors: [ + Color(0xFFDA4453), + Color(0xFF89216B), + ]), + _GradientSample(name: 'Evening Night', colors: [ + Color(0xFF005AA7), + Color(0xFFFFFDE4), + ]), + _GradientSample(name: 'Magic', colors: [ + Color(0xFF59C173), + Color(0xFFa17fe0), + Color(0xFF5D26C1), + ]), + _GradientSample(name: 'Margo', colors: [ + Color(0xFFFFEFBA), + Color(0xFFFFFFFF), + ]), + _GradientSample(name: 'Blue Raspberry', colors: [ + Color(0xFF00B4DB), + Color(0xFF0083B0), + ]), + _GradientSample(name: 'Citrus Peel', colors: [ + Color(0xFFFDC830), + Color(0xFFF37335), + ]), + _GradientSample(name: 'Sin City Red', colors: [ + Color(0xFFED213A), + Color(0xFF93291E), + ]), + _GradientSample(name: 'Rastafari', colors: [ + Color(0xFF1E9600), + Color(0xFFFFF200), + Color(0xFFFF0000), + ]), + _GradientSample(name: 'Summer Dog', colors: [ + Color(0xFFa8ff78), + Color(0xFF78ffd6), + ]), + _GradientSample(name: 'Wiretap', colors: [ + Color(0xFF8A2387), + Color(0xFFE94057), + Color(0xFFF27121), + ]), + _GradientSample(name: 'Burning Orange', colors: [ + Color(0xFFFF416C), + Color(0xFFFF4B2B), + ]), + _GradientSample(name: 'Ultra Voilet', colors: [ + Color(0xFF654ea3), + Color(0xFFeaafc8), + ]), + _GradientSample(name: 'By Design', colors: [ + Color(0xFF009FFF), + Color(0xFFec2F4B), + ]), + _GradientSample(name: 'Kyoo Tah', colors: [ + Color(0xFF544a7d), + Color(0xFFffd452), + ]), + _GradientSample(name: 'Kye Meh', colors: [ + Color(0xFF8360c3), + Color(0xFF2ebf91), + ]), + _GradientSample(name: 'Kyoo Pal', colors: [ + Color(0xFFdd3e54), + Color(0xFF6be585), + ]), + _GradientSample(name: 'Metapolis', colors: [ + Color(0xFF659999), + Color(0xFFf4791f), + ]), + _GradientSample(name: 'Flare', colors: [ + Color(0xFFf12711), + Color(0xFFf5af19), + ]), + _GradientSample(name: 'Witching Hour', colors: [ + Color(0xFFc31432), + Color(0xFF240b36), + ]), + _GradientSample(name: 'Azur Lane', colors: [ + Color(0xFF7F7FD5), + Color(0xFF86A8E7), + Color(0xFF91EAE4), + ]), + _GradientSample(name: 'Neuromancer', colors: [ + Color(0xFFf953c6), + Color(0xFFb91d73), + ]), + _GradientSample(name: 'Harvey', colors: [ + Color(0xFF1f4037), + Color(0xFF99f2c8), + ]), + _GradientSample(name: 'Amin', colors: [ + Color(0xFF8E2DE2), + Color(0xFF4A00E0), + ]), + _GradientSample(name: 'Memariani', colors: [ + Color(0xFFaa4b6b), + Color(0xFF6b6b83), + Color(0xFF3b8d99), + ]), + _GradientSample(name: 'Yoda', colors: [ + Color(0xFFFF0099), + Color(0xFF493240), + ]), + _GradientSample(name: 'Cool Sky', colors: [ + Color(0xFF2980B9), + Color(0xFF6DD5FA), + Color(0xFFFFFFFF), + ]), + _GradientSample(name: 'Dark Ocean', colors: [ + Color(0xFF373B44), + Color(0xFF4286f4), + ]), + _GradientSample(name: 'Evening Sunshine', colors: [ + Color(0xFFb92b27), + Color(0xFF1565C0), + ]), + _GradientSample(name: 'JShine', colors: [ + Color(0xFF12c2e9), + Color(0xFFc471ed), + Color(0xFFf64f59), + ]), + _GradientSample(name: 'Moonlit Asteroid', colors: [ + Color(0xFF0F2027), + Color(0xFF203A43), + Color(0xFF2C5364), + ]), + _GradientSample(name: 'MegaTron', colors: [ + Color(0xFFC6FFDD), + Color(0xFFFBD786), + Color(0xFFf7797d), + ]), + _GradientSample(name: 'Cool Blues', colors: [ + Color(0xFF2193b0), + Color(0xFF6dd5ed), + ]), + _GradientSample(name: 'Piggy Pink', colors: [ + Color(0xFFee9ca7), + Color(0xFFffdde1), + ]), + _GradientSample(name: 'Grade Grey', colors: [ + Color(0xFFbdc3c7), + Color(0xFF2c3e50), + ]), + _GradientSample(name: 'Telko', colors: [ + Color(0xFFF36222), + Color(0xFF5CB644), + Color(0xFF007FC3), + ]), + _GradientSample(name: 'Zenta', colors: [ + Color(0xFF2A2D3E), + Color(0xFFFECB6E), + ]), + _GradientSample(name: 'Electric Peacock', colors: [ + Color(0xFF8a2be2), + Color(0xFF0000cd), + Color(0xFF228b22), + Color(0xFFccff00), + ]), + _GradientSample(name: 'Under Blue Green', colors: [ + Color(0xFF051937), + Color(0xFF004d7a), + Color(0xFF008793), + Color(0xFF00bf72), + Color(0xFFa8eb12), + ]), + _GradientSample(name: 'Lensod', colors: [ + Color(0xFF6025F5), + Color(0xFFFF5555), + ]), + _GradientSample(name: 'Newspaper', colors: [ + Color(0xFF8a2be2), + Color(0xFFffa500), + Color(0xFFf8f8ff), + ]), + _GradientSample(name: 'Dark Blue Gradient', colors: [ + Color(0xFF2774ae), + Color(0xFF002E5D), + Color(0xFF002E5D), + ]), + _GradientSample(name: 'Dark Blu Two', colors: [ + Color(0xFF004680), + Color(0xFF4484BA), + ]), + _GradientSample(name: 'Lemon Lime', colors: [ + Color(0xFF7ec6bc), + Color(0xFFebe717), + ]), + _GradientSample(name: 'Beleko', colors: [ + Color(0xFFff1e56), + Color(0xFFf9c942), + Color(0xFF1e90ff), + ]), + _GradientSample(name: 'Mango Papaya', colors: [ + Color(0xFFde8a41), + Color(0xFF2ada53), + ]), + _GradientSample(name: 'Unicorn Rainbow', colors: [ + Color(0xFFf7f0ac), + Color(0xFFacf7f0), + Color(0xFFf0acf7), + ]), + _GradientSample(name: 'Flame', colors: [ + Color(0xFFff0000), + Color(0xFFfdcf58), + ]), + _GradientSample(name: 'Blue Red', colors: [ + Color(0xFF36B1C7), + Color(0xFF960B33), + ]), + _GradientSample(name: 'Twitter', colors: [ + Color(0xFF1DA1F2), + Color(0xFF009ffc), + ]), + _GradientSample(name: 'Blooze', colors: [ + Color(0xFF6da6be), + Color(0xFF4b859e), + Color(0xFF6da6be), + ]), + _GradientSample(name: 'Blue Slate', colors: [ + Color(0xFFB5B9FF), + Color(0xFF2B2C49), + ]), + _GradientSample(name: 'Space Light Green', colors: [ + Color(0xFF9FA0A8), + Color(0xFF5C7852), + ]), + _GradientSample(name: 'Flower', colors: [ + Color(0xFFDCFFBD), + Color(0xFFCC86D1), + ]), + _GradientSample(name: 'Elate The Euge', colors: [ + Color(0xFF8BDEDA), + Color(0xFF43ADD0), + Color(0xFF998EE0), + Color(0xFFE17DC2), + Color(0xFFEF9393), + ]), + _GradientSample(name: 'Peach Sea', colors: [ + Color(0xFFE6AE8C), + Color(0xFFA8CECF), + ]), + _GradientSample(name: 'Abbas', colors: [ + Color(0xFF00fff0), + Color(0xFF0083fe), + ]), + _GradientSample(name: 'Winter Woods', colors: [ + Color(0xFF333333), + Color(0xFFa2ab58), + Color(0xFFA43931), + ]), + _GradientSample(name: 'Ameena', colors: [ + Color(0xFF0c0c6d), + Color(0xFFde512b), + Color(0xFF98d0c1), + Color(0xFF5bb226), + Color(0xFF023c0d), + ]), + _GradientSample(name: 'Emerald Sea', colors: [ + Color(0xFF05386b), + Color(0xFF5cdb95), + ]), + _GradientSample(name: 'Bleem', colors: [ + Color(0xFF4284DB), + Color(0xFF29EAC4), + ]), + _GradientSample(name: 'Coffee Gold', colors: [ + Color(0xFF554023), + Color(0xFFc99846), + ]), + _GradientSample(name: 'Compass', colors: [ + Color(0xFF516b8b), + Color(0xFF056b3b), + ]), + _GradientSample(name: 'Andreuzza\'s', colors: [ + Color(0xFFD70652), + Color(0xFFFF025E), + ]), + _GradientSample(name: 'Moonwalker', colors: [ + Color(0xFF152331), + Color(0xFF000000), + ]), + _GradientSample(name: 'Whinehouse', colors: [ + Color(0xFFf7f7f7), + Color(0xFFb9a0a0), + Color(0xFF794747), + Color(0xFF4e2020), + Color(0xFF111111), + ]), + _GradientSample(name: 'Hyper Blue', colors: [ + Color(0xFF59CDE9), + Color(0xFF0A2A88), + ]), + _GradientSample(name: 'Racker', colors: [ + Color(0xFFEB0000), + Color(0xFF95008A), + Color(0xFF3300FC), + ]), + _GradientSample(name: 'After the Rain', colors: [ + Color(0xFFff75c3), + Color(0xFFffa647), + Color(0xFFffe83f), + Color(0xFF9fff5b), + Color(0xFF70e2ff), + Color(0xFFcd93ff), + ]), + _GradientSample(name: 'Neon Green', colors: [ + Color(0xFF81ff8a), + Color(0xFF64965e), + ]), + _GradientSample(name: 'Dusty Grass', colors: [ + Color(0xFFd4fc79), + Color(0xFF96e6a1), + ]), + _GradientSample(name: 'Visual Blue', colors: [ + Color(0xFF003d4d), + Color(0xFF00c996), + ]), +]; diff --git a/lib/main.dart b/lib/main.dart index 6f0faa7..212f409 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,6 +5,7 @@ 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/view_models/gradient_view_model.dart'; import 'package:provider/provider.dart'; import 'package:url_strategy/url_strategy.dart'; @@ -39,7 +40,10 @@ class _MyAppState extends State { @override Widget build(BuildContext context) { return OrientationBuilder(builder: (context, orientation) { - final width = MediaQuery.of(context).size.width; + final screenSize = MediaQuery.of(context).size; + + final width = screenSize.width; + final height = screenSize.height; return MaterialApp( title: AppStrings.appTitle, @@ -48,8 +52,16 @@ class _MyAppState extends State { home: AppDimensions( orientation: orientation, screenWidth: width, - child: ChangeNotifierProvider.value( - value: gradientViewModel, + screenHeight: height, + child: MultiProvider( + providers: [ + ChangeNotifierProvider.value( + value: gradientViewModel, + ), + Provider.value( + value: Analytics(), + ), + ], child: const HomeScreen(), ), ), diff --git a/lib/models/abstract_gradient.dart b/lib/models/abstract_gradient.dart index 2fbf5af..d50b364 100644 --- a/lib/models/abstract_gradient.dart +++ b/lib/models/abstract_gradient.dart @@ -2,11 +2,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_gradient_generator/data/app_typedefs.dart'; import 'package:flutter_gradient_generator/enums/gradient_direction.dart'; import 'package:flutter_gradient_generator/enums/gradient_style.dart'; +import 'package:meta/meta.dart'; /// A 2D gradient. /// -/// This is an abstract class that allows holds information about a gradient. -/// This information include: +/// This is an abstract class that holds information about a gradient. +/// +/// This information includes: /// - [ColorAndStop]s /// - Direction /// - Style @@ -53,11 +55,26 @@ abstract class AbstractGradient { /// Returns a [String] representation of the Gradient String toWidgetString(); + /// Returns a [Function] that converts the a list of [Color]s and a list of + /// [Stop]s to a [Gradient] from Flutter's painting library. + /// + /// The purpose of this method is to decouple the Flutter Gradient conversion + /// from the [Color]s and [Stop]s of the child class where the + /// conversion is done. This will allow the reuse of the logic for other + /// instances like the gradient samples. + @mustBeOverridden + FlutterGradientConverter getFlutterGradientConverter(); + /// Returns a [Gradient] from Flutter's painting library /// - /// This should use the [getColorList] and [getStopListForFlutterCode] methods - /// for the colors and stops respectively - Gradient toFlutterGradient(); + /// Do not override this method. Instead, override [getFlutterGradientConverter] + @nonVirtual + Gradient toFlutterGradient() { + return getFlutterGradientConverter().call( + colors: getColorList(), + stops: getStopListForFlutterCode(), + ); + } Map toJson() { return { diff --git a/lib/models/linear_style_gradient.dart b/lib/models/linear_style_gradient.dart index 3f7b845..2f2712d 100644 --- a/lib/models/linear_style_gradient.dart +++ b/lib/models/linear_style_gradient.dart @@ -106,13 +106,13 @@ class LinearStyleGradient extends AbstractGradient { } @override - Gradient toFlutterGradient() { - return LinearGradient( - colors: getColorList(), - begin: beginAlignment, - end: endAlignment, - stops: getStopListForFlutterCode(), - ); + FlutterGradientConverter getFlutterGradientConverter() { + return ({required colors, stops}) => LinearGradient( + colors: colors, + begin: beginAlignment, + end: endAlignment, + stops: stops, + ); } @override diff --git a/lib/models/radial_style_gradient.dart b/lib/models/radial_style_gradient.dart index 9f4f7f9..a3686fd 100644 --- a/lib/models/radial_style_gradient.dart +++ b/lib/models/radial_style_gradient.dart @@ -70,12 +70,12 @@ class RadialStyleGradient extends AbstractGradient { } @override - Gradient toFlutterGradient() { - return RadialGradient( - colors: getColorList(), - center: centerAlignment, - stops: getStopListForFlutterCode(), - ); + FlutterGradientConverter getFlutterGradientConverter() { + return ({required colors, stops}) => RadialGradient( + colors: colors, + center: centerAlignment, + stops: stops, + ); } @override diff --git a/lib/models/sweep_style_gradient.dart b/lib/models/sweep_style_gradient.dart index b5f075c..12c208f 100644 --- a/lib/models/sweep_style_gradient.dart +++ b/lib/models/sweep_style_gradient.dart @@ -70,12 +70,14 @@ class SweepStyleGradient extends AbstractGradient { } @override - Gradient toFlutterGradient() { - return SweepGradient( - colors: getColorList(), - center: centerAlignment, - stops: getStopListForFlutterCode(), - ); + FlutterGradientConverter getFlutterGradientConverter() { + return ({required colors, stops}) { + return SweepGradient( + colors: colors, + center: centerAlignment, + stops: stops, + ); + }; } @override diff --git a/lib/ui/screens/home_screen.dart b/lib/ui/screens/home_screen.dart index 64fb612..fe20d3f 100644 --- a/lib/ui/screens/home_screen.dart +++ b/lib/ui/screens/home_screen.dart @@ -1,9 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_gradient_generator/data/app_dimensions.dart'; +import 'package:flutter_gradient_generator/ui/screens/sections/left_section.dart'; +import 'package:flutter_gradient_generator/ui/widgets/header/header.dart'; import 'package:flutter_gradient_generator/view_models/gradient_view_model.dart'; import 'package:flutter_gradient_generator/ui/screens/sections/generator_section.dart'; import 'package:flutter_gradient_generator/ui/screens/sections/preview_section.dart'; -import 'package:flutter_gradient_generator/ui/widgets/footer/footer_widget.dart'; import 'package:provider/provider.dart'; class HomeScreen extends StatefulWidget { @@ -31,33 +32,45 @@ class HomeScreenState extends State { Widget build(BuildContext context) { final appDimensions = AppDimensions.of(context); + //TODO: Handle portrait UI + // ignore: unused_local_variable final displayPortrait = appDimensions.shouldDisplayPortraitUI; + final generatorScreenWidth = appDimensions.generatorScreenWidth; + final previewSectionWidth = appDimensions.previewSectionWidth; + return Focus( child: Scaffold( - body: Row( - crossAxisAlignment: displayPortrait - ? CrossAxisAlignment.center - : CrossAxisAlignment.stretch, - children: [ - Flexible( - flex: displayPortrait ? 1 : 0, - child: const Stack( - alignment: Alignment.bottomCenter, - children: [ - Align( - alignment: Alignment.topCenter, - child: GeneratorSection(), + body: GridPaper( + color: Colors.transparent, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Header(), + Expanded( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: generatorScreenWidth, + child: const LeftSection(), + ), + SizedBox( + width: previewSectionWidth, + child: const PreviewSection.landscape(), + ), + SizedBox( + width: generatorScreenWidth, + child: const GeneratorSection(), + ), + ], ), - FooterWidget() - ], - ), - ), - if (!displayPortrait) - const Flexible( - child: PreviewSection.landscape(), + ), ), - ], + ], + ), ), ), ); diff --git a/lib/ui/screens/sections/generator_section.dart b/lib/ui/screens/sections/generator_section.dart index 3f39170..bf2ac52 100644 --- a/lib/ui/screens/sections/generator_section.dart +++ b/lib/ui/screens/sections/generator_section.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gradient_generator/data/app_dimensions.dart'; -import 'package:flutter_gradient_generator/ui/screens/sections/preview_section.dart'; import 'package:flutter_gradient_generator/ui/widgets/buttons/copy_gradient_button.dart'; -import 'package:flutter_gradient_generator/ui/widgets/generator_widgets/app_title_widget.dart'; -import 'package:flutter_gradient_generator/ui/widgets/generator_widgets/selection_widgets/selection_widgets.dart'; +import 'package:flutter_gradient_generator/ui/widgets/selection_widgets/color_and_stop_selection_widget.dart'; +import 'package:flutter_gradient_generator/ui/widgets/selection_widgets/direction_selection_widget.dart'; +import 'package:flutter_gradient_generator/ui/widgets/selection_widgets/style_selection_widget.dart'; class GeneratorSection extends StatelessWidget { const GeneratorSection({ @@ -12,59 +11,40 @@ class GeneratorSection extends StatelessWidget { @override Widget build(BuildContext context) { - final appDimensions = AppDimensions.of(context); - final isPortrait = appDimensions.shouldDisplayPortraitUI; - - final generatorScreenHorizontalPadding = - appDimensions.generatorScreenHorizontalPadding; - final generatorScreenVerticalPadding = - appDimensions.generatorScreenVerticalPadding; + const divider = Divider( + thickness: 0.5, + height: 0, + ); return SingleChildScrollView( - child: Padding( - padding: EdgeInsets.symmetric( - horizontal: generatorScreenHorizontalPadding, - vertical: generatorScreenVerticalPadding, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Flex( - /// This results in: - /// - a [Column] (veritical [Flex]]) for portrait mode - /// - a [Row] (horizontal [Flex]]) for landscape mode - direction: isPortrait ? Axis.vertical : Axis.horizontal, - mainAxisSize: MainAxisSize.min, - children: [ - AppTitleWidget( - forPortrait: isPortrait, - ), - if (isPortrait) ...[ - const SizedBox(height: 40), - const AspectRatio( - aspectRatio: 16 / 9, - child: PreviewSection.portrait(), - ), - ], - ], - ), - const SizedBox(height: 40), - const StyleSelectionWidget(), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const SizedBox(height: 16), + const StyleSelectionWidget(), + const SizedBox(height: 16), + divider, + const SizedBox(height: 16), + DirectionSelectionWidget(), + const SizedBox(height: 16), + divider, + // Using 6 instead of 16 to match the design due to additional space + // added by the plus button in the `ColorAndStopSelectionWidget` + const SizedBox(height: 6), - /// Ideally the spacing should be 24 but the [ColorAndStopSelectionWidget] - /// has an action button that gives it an extra height. - /// - /// So the height is adjusted to 32 to match the spacing between - /// [DirectionSelectionWidget] and [ColorAndStopSelectionWidget] - const SizedBox(height: 32), - DirectionSelectionWidget(), - const SizedBox(height: 24), - const ColorAndStopSelectionWidget(), - const SizedBox(height: 48), - const CopyGradientButton(), - const SizedBox(height: 100), - ], - ), + ///TODO: Improve scrolling experience and performance by showing + /// `ColorAndStop` widgets on demand + const ColorAndStopSelectionWidget(), + // Using 6 instead of 16 to match the design due to additional space + // added by the plus button in the `ColorAndStopSelectionWidget` + const SizedBox(height: 6), + divider, + const SizedBox(height: 16), + const CopyGradientButton(), + const SizedBox(height: 16), + divider, + ], ), ); } diff --git a/lib/ui/screens/sections/left_section.dart b/lib/ui/screens/sections/left_section.dart new file mode 100644 index 0000000..511b600 --- /dev/null +++ b/lib/ui/screens/sections/left_section.dart @@ -0,0 +1,185 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gradient_generator/data/app_dimensions.dart'; +import 'package:flutter_gradient_generator/data/app_strings.dart'; +import 'package:flutter_gradient_generator/ui/widgets/selection_widgets/samples_selection_widget.dart'; +import 'package:flutter_gradient_generator/utils/analytics.dart'; +import 'package:provider/provider.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class LeftSection extends StatelessWidget { + const LeftSection({super.key}); + + @override + Widget build(BuildContext context) { + final appDimensions = AppDimensions.of(context); + + final generatorScreenHorizontalPadding = + appDimensions.generatorScreenHorizontalPadding; + + final sampleSectionVerticalPadding = + appDimensions.sampleSectionVerticalPadding; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: sampleSectionVerticalPadding, + ), + const SampleSelectionWidget(), + SizedBox( + height: sampleSectionVerticalPadding, + ), + const Divider( + thickness: 0.5, + height: 0, + ), + Flexible( + child: Align( + alignment: Alignment.bottomLeft, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: generatorScreenHorizontalPadding), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SimpleTextLink( + text: AppStrings.requestFeature, + onPressed: () { + final analytics = context.read(); + + analytics.logFeatureRequestButtonClickEvent(); + + launchUrl(Uri.parse(AppStrings.featureRequestUrl)); + }, + ), + SizedBox( + height: sampleSectionVerticalPadding, + ), + SimpleTextLink( + text: AppStrings.reportBug, + onPressed: () { + final analytics = context.read(); + + analytics.logBugReportButtonClickEvent(); + + launchUrl(Uri.parse(AppStrings.bugReportUrl)); + }, + ), + SizedBox( + height: sampleSectionVerticalPadding, + ), + SimpleTextLink( + text: AppStrings.viewSourceCodeOnGitHub, + onPressed: () { + final analytics = context.read(); + + analytics.logViewSourceCodeOnGitHubButtonClickEvent(); + + launchUrl(Uri.parse(AppStrings.githubUrl)); + }, + ), + SizedBox( + height: sampleSectionVerticalPadding, + ), + const BuiltByVictorEronmosele(), + ], + ), + ), + ), + ), + const SizedBox( + height: 16.0, + ), + const Divider( + thickness: 0.5, + height: 0, + ), + ], + ); + } +} + +class BuiltByVictorEronmosele extends StatefulWidget { + const BuiltByVictorEronmosele({ + super.key, + }); + + @override + State createState() => + _BuiltByVictorEronmoseleState(); +} + +class _BuiltByVictorEronmoseleState extends State { + late final TapGestureRecognizer _tapGestureRecognizer; + + @override + void initState() { + super.initState(); + + _tapGestureRecognizer = TapGestureRecognizer() + ..onTap = () { + final analytics = context.read(); + + analytics.logVictorEronmoseleClickEvent(); + + launchUrl(Uri.parse(AppStrings.victorEronmoseleWebsiteUrl)); + }; + } + + @override + void dispose() { + _tapGestureRecognizer.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final textStyle = Theme.of(context).textTheme.labelLarge?.copyWith( + fontWeight: FontWeight.bold, + ); + + return Text.rich( + TextSpan( + children: [ + const TextSpan( + text: '${AppStrings.builtBy} ', + ), + TextSpan( + text: AppStrings.victorEronmosele, + style: textStyle?.copyWith( + decoration: TextDecoration.underline, + ), + recognizer: _tapGestureRecognizer, + ), + ], + ), + style: textStyle, + ); + } +} + +class SimpleTextLink extends StatelessWidget { + const SimpleTextLink({ + super.key, + required this.text, + required this.onPressed, + }); + + final String text; + final void Function() onPressed; + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onPressed, + child: Text( + text, + style: Theme.of(context).textTheme.labelLarge?.copyWith( + decoration: TextDecoration.underline, + ), + ), + ); + } +} diff --git a/lib/ui/screens/sections/preview_section.dart b/lib/ui/screens/sections/preview_section.dart index 5ec5484..26cfe0a 100644 --- a/lib/ui/screens/sections/preview_section.dart +++ b/lib/ui/screens/sections/preview_section.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_gradient_generator/data/app_colors.dart'; import 'package:flutter_gradient_generator/models/abstract_gradient.dart'; import 'package:flutter_gradient_generator/view_models/gradient_view_model.dart'; import 'package:provider/provider.dart'; @@ -24,15 +25,33 @@ class PreviewSection extends StatelessWidget { @override Widget build(BuildContext context) { + final screenSize = MediaQuery.of(context).size; + + final screenHeight = screenSize.height; + + final previewWidgetSize = screenHeight / 1.5; + final gradient = context.select( (GradientViewModel viewModel) => viewModel.gradient); final flutterGradient = gradient.toFlutterGradient(); - return Container( - decoration: BoxDecoration( - gradient: flutterGradient, - borderRadius: BorderRadius.circular(borderRadius), + return Center( + child: Container( + color: AppColors.previewBackground, + child: Center( + child: Container( + constraints: BoxConstraints.tight( + Size.square( + previewWidgetSize, + ), + ), + decoration: BoxDecoration( + gradient: flutterGradient, + borderRadius: BorderRadius.circular(borderRadius), + ), + ), + ), ), ); } diff --git a/lib/ui/util/color_picker/color_picker_interface.dart b/lib/ui/util/color_picker/color_picker_interface.dart deleted file mode 100644 index 36749db..0000000 --- a/lib/ui/util/color_picker/color_picker_interface.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:flutter/material.dart'; - -abstract interface class ColorPickerInterface { - void selectColor( - {required BuildContext context, - required Color currentColor, - required void Function(Color) onColorSelected}); -} diff --git a/lib/ui/util/random_color_generator/abstract_random_color_generator.dart b/lib/ui/util/random_color_generator/abstract_random_color_generator.dart deleted file mode 100644 index e8e5d45..0000000 --- a/lib/ui/util/random_color_generator/abstract_random_color_generator.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:flutter_gradient_generator/data/app_typedefs.dart'; - -abstract interface class AbstractRandomColorGenerator { - List - getRandomColorAndStopsOfCurrentGradientColorAndStopListLength({ - required List currentStopList, - }); -} diff --git a/lib/ui/util/random_color_generator/random_color_generator.dart b/lib/ui/util/random_color_generator/random_color_generator.dart deleted file mode 100644 index 6e0a41b..0000000 --- a/lib/ui/util/random_color_generator/random_color_generator.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_gradient_generator/data/app_typedefs.dart'; -import 'package:flutter_gradient_generator/ui/util/random_color_generator/abstract_random_color_generator.dart'; -import 'dart:math'; - -class RandomColorGenerator implements AbstractRandomColorGenerator { - const RandomColorGenerator(); - - @override - List - getRandomColorAndStopsOfCurrentGradientColorAndStopListLength( - {required List currentStopList}) { - final List colorList = - Colors.primaries.map((color) => color.shade500).toList(); - - final int colorListLength = colorList.length; - - final int currentGradientColorAndStopListLength = currentStopList.length; - - final Random random = Random(); - - final List randomColorsAndStops = []; - - for (int i = 0; i < currentGradientColorAndStopListLength; i++) { - final int randomIndex = random.nextInt(colorListLength); - - final Color randomColor = colorList.elementAt(randomIndex); - - /// The stop position of the color in the gradient. - final int stop = currentStopList.elementAt(i); - - randomColorsAndStops.add( - (color: randomColor, stop: stop), - ); - } - - return randomColorsAndStops; - } -} diff --git a/lib/ui/widgets/buttons/copy_gradient_button.dart b/lib/ui/widgets/buttons/copy_gradient_button.dart index 1a7ef68..cc20422 100644 --- a/lib/ui/widgets/buttons/copy_gradient_button.dart +++ b/lib/ui/widgets/buttons/copy_gradient_button.dart @@ -1,11 +1,10 @@ -import 'package:firebase_analytics/firebase_analytics.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_gradient_generator/data/app_colors.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/utils/analytics.dart'; import 'package:flutter_gradient_generator/view_models/gradient_view_model.dart'; import 'package:provider/provider.dart'; @@ -57,41 +56,48 @@ class _CopyGradientButtonState extends State { final wideButtonWidth = appDimensions.wideButtonWidth; final wideButtonHeight = appDimensions.wideButtonHeight; + final generatorScreenHorizontalPadding = + appDimensions.generatorScreenHorizontalPadding; + final gradient = context.watch().gradient; final generatedCode = gradient.toWidgetString(); - return TextButton( - onPressed: () async { - await Clipboard.setData(ClipboardData(text: generatedCode)); - - /// Log event to Firebase Analytics if not in debug mode - if (!kDebugMode) { - await FirebaseAnalytics.instance.logEvent( - name: AppStrings.gradientGeneratedFirebaseAnalyticsKey, - parameters: gradient.toJson()); - } - - setState(() { - _showCopiedText = true; - }); - - await Future.delayed(const Duration(seconds: 2)); - - setState(() { - _showCopiedText = false; - }); - }, - style: ButtonStyle( - backgroundColor: MaterialStateProperty.resolveWith(_getBackgroundColor), - foregroundColor: MaterialStateProperty.resolveWith(_getForegroundColor), - textStyle: MaterialStateProperty.all(TextStyle( - fontWeight: FontWeight.bold, - fontFamily: AppFonts.getFontFamily(context))), - padding: MaterialStateProperty.all(EdgeInsets.all(wideButtonPadding)), - fixedSize: MaterialStateProperty.all( - (Size(wideButtonWidth, wideButtonHeight))), + return Padding( + padding: EdgeInsets.symmetric( + horizontal: generatorScreenHorizontalPadding, + ), + child: TextButton( + onPressed: () async { + final analytics = context.read(); + + await Clipboard.setData(ClipboardData(text: generatedCode)); + + analytics.logGradientGeneratedEvent(gradient); + + setState(() { + _showCopiedText = true; + }); + + await Future.delayed(const Duration(seconds: 2)); + + setState(() { + _showCopiedText = false; + }); + }, + style: ButtonStyle( + backgroundColor: + MaterialStateProperty.resolveWith(_getBackgroundColor), + foregroundColor: + MaterialStateProperty.resolveWith(_getForegroundColor), + textStyle: MaterialStateProperty.all(TextStyle( + fontWeight: FontWeight.bold, + fontFamily: AppFonts.getFontFamily(context))), + padding: MaterialStateProperty.all(EdgeInsets.all(wideButtonPadding)), + fixedSize: MaterialStateProperty.all( + (Size(wideButtonWidth, wideButtonHeight))), + ), + child: Text(_buttonText), ), - child: Text(_buttonText), ); } } diff --git a/lib/ui/widgets/footer/footer_widget.dart b/lib/ui/widgets/footer/footer_widget.dart deleted file mode 100644 index 58cec9e..0000000 --- a/lib/ui/widgets/footer/footer_widget.dart +++ /dev/null @@ -1,88 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_gradient_generator/data/app_dimensions.dart'; -import 'package:flutter_gradient_generator/data/app_strings.dart'; -import 'package:url_launcher/url_launcher.dart'; - -class FooterWidget extends StatefulWidget { - const FooterWidget({ - Key? key, - }) : super(key: key); - - @override - State createState() => _FooterWidgetState(); -} - -class _FooterWidgetState extends State { - final _builtTapRecognizer = TapGestureRecognizer(); - final _nameTapGestureRecognizer = TapGestureRecognizer(); - - @override - void initState() { - super.initState(); - _builtTapRecognizer.onTap = () { - launchUrl(Uri.parse(AppStrings.githubUrl)); - }; - - _nameTapGestureRecognizer.onTap = () { - launchUrl(Uri.parse(AppStrings.personalWebsiteUrl)); - }; - } - - @override - void dispose() { - super.dispose(); - _builtTapRecognizer.dispose(); - _nameTapGestureRecognizer.dispose(); - } - - @override - Widget build(BuildContext context) { - final appDimensions = AppDimensions.of(context); - - final generatorScreenContentWidth = - appDimensions.generatorScreenContentWidth; - final generatorScreenHorizontalPadding = - appDimensions.generatorScreenHorizontalPadding; - final generatorScreenVerticalPadding = - appDimensions.generatorScreenVerticalPadding; - - return Container( - color: Theme.of(context).scaffoldBackgroundColor, - width: generatorScreenContentWidth, - child: Padding( - padding: EdgeInsets.symmetric( - horizontal: generatorScreenHorizontalPadding, - vertical: generatorScreenVerticalPadding / 2, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text.rich( - TextSpan( - children: [ - TextSpan( - text: '${AppStrings.built} ', - style: - const TextStyle(decoration: TextDecoration.underline), - recognizer: _builtTapRecognizer, - ), - const TextSpan( - text: AppStrings.by, - ), - TextSpan( - text: ' ${AppStrings.victorEronmosele}', - style: - const TextStyle(decoration: TextDecoration.underline), - recognizer: _nameTapGestureRecognizer, - ), - ], - ), - style: const TextStyle(fontWeight: FontWeight.bold), - ) - ], - ), - ), - ); - } -} diff --git a/lib/ui/widgets/generator_widgets/app_title_widget.dart b/lib/ui/widgets/generator_widgets/app_title_widget.dart deleted file mode 100644 index 6d57a7a..0000000 --- a/lib/ui/widgets/generator_widgets/app_title_widget.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_gradient_generator/data/app_strings.dart'; - -class AppTitleWidget extends StatelessWidget { - const AppTitleWidget({ - Key? key, - required this.forPortrait, - }) : super(key: key); - - final bool forPortrait; - - @override - Widget build(BuildContext context) { - final titleToDisplay = - forPortrait ? AppStrings.appTitle : AppStrings.appTitleNewLine; - - return Text( - titleToDisplay.toUpperCase(), - textAlign: TextAlign.left, - style: TextStyle( - fontSize: forPortrait ? 20.0 : 24.0, - fontWeight: FontWeight.bold, - ), - ); - } -} diff --git a/lib/ui/widgets/generator_widgets/selection_container_widget.dart b/lib/ui/widgets/generator_widgets/selection_container_widget.dart deleted file mode 100644 index b067ae9..0000000 --- a/lib/ui/widgets/generator_widgets/selection_container_widget.dart +++ /dev/null @@ -1,137 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_gradient_generator/data/app_dimensions.dart'; - -/// Holds the selection widgets -/// -/// The selection container contains: -/// - a title -/// - a selection widget -/// -/// It allows the user to expand and collapse the selection widget -class SelectionWidgetContainer extends StatefulWidget { - const SelectionWidgetContainer( - {super.key, - required this.titleWidgetInformation, - required this.selectionWidget}); - - /// Holds the information for the title widget - /// - /// It holds the following: - /// - /// - a [mainTitle] String which is the text describing the selection widget - /// - /// - an [trailingActionWidget] widget which is an extra button to the right of the title. - /// Set to [SizedBox.shrink()] if you don't want an action button - /// - final ({ - String mainTitle, - Widget trailingActionWidget - }) titleWidgetInformation; - final Widget selectionWidget; - - @override - State createState() => - _SelectionWidgetContainerState(); -} - -class _SelectionWidgetContainerState extends State - with SingleTickerProviderStateMixin { - static final Animatable _easeInTween = - CurveTween(curve: Curves.easeIn); - static final Animatable _openAndCloseTween = - Tween(begin: -0.25, end: 0); - - bool isExpanded = true; - - late AnimationController _animationController; - late Animation _expansionIconTurns; - late Animation _selectionWidgetOpacity; - late Animation _selectionWidgetSizeFactor; - - @override - void initState() { - super.initState(); - _animationController = AnimationController( - duration: const Duration(milliseconds: 200), vsync: this); - _expansionIconTurns = - _animationController.drive(_openAndCloseTween.chain(_easeInTween)); - _selectionWidgetOpacity = _animationController.drive(_easeInTween); - _selectionWidgetSizeFactor = _animationController.drive(_easeInTween); - - _animationController.value = 1.0; - - _animationController.forward(); - } - - void _toggleExpansion() { - isExpanded = !isExpanded; - - if (isExpanded) { - _animationController.forward(); - } else { - _animationController.reverse(); - } - } - - @override - Widget build(BuildContext context) { - final AppDimensions appDimensions = AppDimensions.of(context); - - final compactButtonMargin = appDimensions.compactButtonMargin; - - final (mainTitle: mainTitle, trailingActionWidget: trailingActionWidget) = - widget.titleWidgetInformation; - - final expansionIconSize = appDimensions.expansionIconSize; - final selectionContainerMainTitleWidth = - appDimensions.selectionContainerMainTitleWidth; - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - decoration: BoxDecoration( - color: Colors.black, - borderRadius: BorderRadius.circular(4.0), - ), - child: RotationTransition( - turns: _expansionIconTurns, - child: GestureDetector( - onTap: _toggleExpansion, - child: Icon( - Icons.expand_more, - size: expansionIconSize, - color: Colors.white, - ))), - ), - SizedBox(width: compactButtonMargin), - SizedBox( - width: selectionContainerMainTitleWidth, - child: Text( - mainTitle, - style: const TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.bold, - ), - ), - ), - SizedBox(width: compactButtonMargin), - trailingActionWidget, - ], - ), - SizeTransition( - sizeFactor: _selectionWidgetSizeFactor, - child: FadeTransition( - opacity: _selectionWidgetOpacity, - child: Column( - children: [const SizedBox(height: 16), widget.selectionWidget], - )), - ), - ], - ); - } -} diff --git a/lib/ui/widgets/generator_widgets/selection_widgets/selection_widgets.dart b/lib/ui/widgets/generator_widgets/selection_widgets/selection_widgets.dart deleted file mode 100644 index f6ce45c..0000000 --- a/lib/ui/widgets/generator_widgets/selection_widgets/selection_widgets.dart +++ /dev/null @@ -1,3 +0,0 @@ -export 'color_and_stop_selection_widget.dart'; -export 'direction_selection_widget.dart'; -export 'style_selection_widget.dart'; diff --git a/lib/ui/widgets/header/header.dart b/lib/ui/widgets/header/header.dart new file mode 100644 index 0000000..1355c39 --- /dev/null +++ b/lib/ui/widgets/header/header.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gradient_generator/ui/widgets/header/widgets/tool_bar.dart'; +import 'package:flutter_gradient_generator/ui/widgets/header/widgets/banner_ad.dart'; + +class Header extends StatefulWidget { + const Header({ + Key? key, + }) : super(key: key); + + @override + State
createState() => _HeaderState(); +} + +class _HeaderState extends State
with SingleTickerProviderStateMixin { + late AnimationController _closeBannerAdController; + late Animation _bannerAdSizeFactor; + + @override + void initState() { + super.initState(); + + _closeBannerAdController = AnimationController( + duration: const Duration(milliseconds: 200), + vsync: this, + ); + _bannerAdSizeFactor = Tween(begin: 1, end: 0).animate( + CurvedAnimation( + parent: _closeBannerAdController, + curve: Curves.easeIn, + ), + ); + } + + @override + void dispose() { + super.dispose(); + + _closeBannerAdController.dispose(); + } + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SizeTransition( + sizeFactor: _bannerAdSizeFactor, + child: BannerAd( + onClose: () { + _closeBannerAdController.forward(); + }, + ), + ), + ToolBar(), + ], + ); + } +} diff --git a/lib/ui/widgets/header/widgets/banner_ad.dart b/lib/ui/widgets/header/widgets/banner_ad.dart new file mode 100644 index 0000000..7a2c65a --- /dev/null +++ b/lib/ui/widgets/header/widgets/banner_ad.dart @@ -0,0 +1,120 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gradient_generator/data/app_colors.dart'; +import 'package:flutter_gradient_generator/data/app_dimensions.dart'; +import 'package:flutter_gradient_generator/data/app_strings.dart'; +import 'package:flutter_gradient_generator/utils/analytics.dart'; +import 'package:flutter_gradient_generator/view_models/gradient_view_model.dart'; +import 'package:provider/provider.dart'; +import 'package:url_launcher/url_launcher.dart'; + +/// TODO: Make this ad dynamic and not hardcoded. +/// Probably use Firebase Remote Config. +/// +/// A banner ad that is shown on the top of the app. +class BannerAd extends StatelessWidget { + const BannerAd({ + super.key, + required this.onClose, + }); + + /// The callback to call when the close button is pressed. + /// + /// It is meant to close the banner ad. + final VoidCallback onClose; + + @override + Widget build(BuildContext context) { + final appDimensions = AppDimensions.of(context); + final gradientViewModel = context.read(); + + final bannerAdHorizontalPadding = appDimensions.bannerAdHorizontalPadding; + + const foregroundColor = AppColors.white; + + const ctaButtonTitle = AppStrings.giveFeedback; + + return Container( + decoration: BoxDecoration( + gradient: gradientViewModel.defaultGradient.toFlutterGradient(), + ), + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: bannerAdHorizontalPadding, + ), + child: Row( + children: [ + /// This accounts for the close button on the right of the banner ad. + /// + /// It enables the actual banner ad message to be centered. + const SizedBox( + width: 24, + ), + Expanded( + child: Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Have thoughts on the website\'s design or features?', + style: Theme.of(context).textTheme.labelLarge?.copyWith( + color: foregroundColor, + ), + textAlign: TextAlign.center, + ), + const SizedBox( + width: 16, + ), + OutlinedButton( + onPressed: () async { + const ctaButtonUrl = AppStrings.feedbackUrl; + + final analytics = context.read(); + + /// TODO: Remove this when the banner ad is dynamic + /// + /// This is only left for now since the banner ad is + /// for giving feedback. + analytics.logFeedbackButtonClickEvent(); + + analytics.logBannerAdCTAButtonClickEvent( + name: ctaButtonTitle, + url: ctaButtonUrl, + ); + + launchUrl( + Uri.parse( + ctaButtonUrl, + ), + ); + }, + style: OutlinedButton.styleFrom( + foregroundColor: foregroundColor, + side: const BorderSide( + color: foregroundColor, + ), + ), + child: const Text( + ctaButtonTitle, + textAlign: TextAlign.center, + ), + ), + ], + ), + ), + ), + CloseButton( + color: foregroundColor, + onPressed: () { + final analytics = context.read(); + + analytics.logBannerAdCloseButtonClickEvent(); + + onClose(); + }, + ) + ], + ), + ), + ); + } +} diff --git a/lib/ui/widgets/header/widgets/tool_bar.dart b/lib/ui/widgets/header/widgets/tool_bar.dart new file mode 100644 index 0000000..c4cab55 --- /dev/null +++ b/lib/ui/widgets/header/widgets/tool_bar.dart @@ -0,0 +1,97 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gradient_generator/data/app_colors.dart'; +import 'package:flutter_gradient_generator/data/app_dimensions.dart'; +import 'package:flutter_gradient_generator/data/app_strings.dart'; +import 'package:flutter_gradient_generator/utils/analytics.dart'; +import 'package:flutter_gradient_generator/view_models/gradient_view_model.dart'; +import 'package:image_downloader_web/image_downloader_web.dart'; +import 'package:provider/provider.dart'; +import 'package:screenshot/screenshot.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class ToolBar extends StatelessWidget { + ToolBar({ + Key? key, + }) : super(key: key); + + final ScreenshotController screenshotController = ScreenshotController(); + + @override + Widget build(BuildContext context) { + final appDimensions = AppDimensions.of(context); + + final generatorScreenHorizontalPadding = + appDimensions.generatorScreenHorizontalPadding; + + return Container( + color: AppColors.toolBar, + height: appDimensions.toolBarHeight, + child: Center( + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: generatorScreenHorizontalPadding, + ), + child: Row( + children: [ + Expanded( + child: InkWell( + onTap: () { + launchUrl( + Uri.parse('/'), + + /// Open in current tab + webOnlyWindowName: '_self', + ); + }, + child: Text( + AppStrings.appTitle, + style: Theme.of(context).textTheme.labelLarge?.copyWith( + color: AppColors.white, + ), + ), + ), + ), + Tooltip( + message: AppStrings.downloadGradientAsImage, + child: IconButton( + onPressed: () async { + final analytics = context.read(); + + final gradientViewModel = + context.read(); + + final gradient = gradientViewModel.gradient; + + final flutterGradient = gradient.toFlutterGradient(); + + final imageBytes = + await screenshotController.captureFromWidget( + Container( + decoration: BoxDecoration( + gradient: flutterGradient, + ), + ), + ); + + await WebImageDownloader.downloadImageFromUInt8List( + uInt8List: imageBytes, + name: 'gradient.png', + ); + + analytics.logGradientDownloadedAsImageEvent(gradient); + }, + icon: const Icon( + Icons.save_alt_outlined, + color: AppColors.toolBarIcon, + ), + iconSize: appDimensions.toolBarIconButtonSize, + hoverColor: AppColors.toolBarIconHover, + focusColor: AppColors.toolBarIconFocus, + ), + ), + ], + ), + ), + )); + } +} diff --git a/lib/ui/widgets/generator_widgets/selection_widgets/color_and_stop_selection_widget.dart b/lib/ui/widgets/selection_widgets/color_and_stop_selection_widget.dart similarity index 82% rename from lib/ui/widgets/generator_widgets/selection_widgets/color_and_stop_selection_widget.dart rename to lib/ui/widgets/selection_widgets/color_and_stop_selection_widget.dart index b6c8ae0..27841c7 100644 --- a/lib/ui/widgets/generator_widgets/selection_widgets/color_and_stop_selection_widget.dart +++ b/lib/ui/widgets/selection_widgets/color_and_stop_selection_widget.dart @@ -3,9 +3,8 @@ import 'package:flutter_gradient_generator/data/app_colors.dart'; import 'package:flutter_gradient_generator/data/app_dimensions.dart'; import 'package:flutter_gradient_generator/data/app_strings.dart'; import 'package:flutter_gradient_generator/view_models/gradient_view_model.dart'; -import 'package:flutter_gradient_generator/ui/widgets/buttons/compact_button.dart'; -import 'package:flutter_gradient_generator/ui/widgets/generator_widgets/selection_container_widget.dart'; -import 'package:flutter_gradient_generator/ui/widgets/generator_widgets/selection_widgets/color_and_stop_selection_widgets/stop_text_box.dart'; +import 'package:flutter_gradient_generator/ui/widgets/selection_widgets/selection_container_widget.dart'; +import 'package:flutter_gradient_generator/ui/widgets/selection_widgets/color_and_stop_selection_widgets/stop_text_box.dart'; import 'package:flutter_gradient_generator/utils/color_and_stop_util.dart'; import 'package:html_color_input/html_color_input.dart'; import 'package:provider/provider.dart'; @@ -35,12 +34,10 @@ class _ColorAndStopSelectionWidgetState @override Widget build(BuildContext context) { return SelectionWidgetContainer( - titleWidgetInformation: getTitleWidgetInformation( - onRandomButtonPressed: () { - gradientViewModelReadOnly.randomizeColors(); - }, - ), + title: AppStrings.colorsAndStops, selectionWidget: getSelectionWidget(), + titleTrailingWidget: getAddColorButton(), + titleBottomMargin: 2.0, ); } @@ -53,13 +50,12 @@ class _ColorAndStopSelectionWidgetState } Widget getAddColorButton() { - return SizedBox( - width: generatorScreenContentWidth, - child: CompactButton.text( + return Tooltip( + message: AppStrings.addColor, + child: IconButton( + icon: const Icon(Icons.add), onPressed: gradientViewModelReadOnly.addNewColor, - backgroundColor: AppColors.grey, - foregroundColor: Colors.black, - text: AppStrings.addColor, + iconSize: 16.0, ), ); } @@ -82,6 +78,7 @@ class _ColorAndStopSelectionWidgetState gradientViewModel.gradient.getColorAndStopList(); return Column( + mainAxisSize: MainAxisSize.min, children: List.generate( colorAndStopList.length, (index) { @@ -99,7 +96,7 @@ class _ColorAndStopSelectionWidgetState padding: const EdgeInsets.symmetric(vertical: 2.0), child: Row( children: [ - HtmlColorInput( + WebColorPicker.builder( key: ValueKey(color), initialColor: color, onInput: (newColor, event) { @@ -108,8 +105,19 @@ class _ColorAndStopSelectionWidgetState currentColorAndStopIndex: index, ); }, - width: compactButtonWidth, - height: compactButtonHeight, + builder: ((context, selectedColor) { + return Container( + width: compactButtonWidth, + height: compactButtonHeight, + decoration: BoxDecoration( + color: selectedColor ?? color, + borderRadius: BorderRadius.circular(4.0), + border: Border.all( + color: AppColors.colorPickerBorder, + ), + ), + ); + }), ), SizedBox( width: compactButtonMargin, @@ -208,30 +216,7 @@ class _ColorAndStopSelectionWidgetState getInstructionTopRow(), const SizedBox(height: 6), getColorAndStopDisplaySection(), - const SizedBox( - height: 8.0, - ), - getAddColorButton(), ], ); } - - ({String mainTitle, CompactButton trailingActionWidget}) - getTitleWidgetInformation( - {required void Function() onRandomButtonPressed}) { - return ( - mainTitle: AppStrings.colorsAndStops, - trailingActionWidget: CompactButton.text( - onPressed: () { - onRandomButtonPressed(); - }, - backgroundColor: Colors.transparent, - foregroundColor: Colors.black, - borderSide: BorderSide( - color: AppColors.grey, - ), - text: AppStrings.random, - ), - ); - } } diff --git a/lib/ui/widgets/generator_widgets/selection_widgets/color_and_stop_selection_widgets/outlined_text_field.dart b/lib/ui/widgets/selection_widgets/color_and_stop_selection_widgets/outlined_text_field.dart similarity index 97% rename from lib/ui/widgets/generator_widgets/selection_widgets/color_and_stop_selection_widgets/outlined_text_field.dart rename to lib/ui/widgets/selection_widgets/color_and_stop_selection_widgets/outlined_text_field.dart index 8b1b60d..aca6c46 100644 --- a/lib/ui/widgets/generator_widgets/selection_widgets/color_and_stop_selection_widgets/outlined_text_field.dart +++ b/lib/ui/widgets/selection_widgets/color_and_stop_selection_widgets/outlined_text_field.dart @@ -42,7 +42,6 @@ class OutlinedTextField extends StatelessWidget { ), keyboardType: keyboardType, textAlign: TextAlign.center, - onSubmitted: onSubmitted, controller: controller, focusNode: focusNode, onTap: onTap, diff --git a/lib/ui/widgets/generator_widgets/selection_widgets/color_and_stop_selection_widgets/stop_text_box.dart b/lib/ui/widgets/selection_widgets/color_and_stop_selection_widgets/stop_text_box.dart similarity index 89% rename from lib/ui/widgets/generator_widgets/selection_widgets/color_and_stop_selection_widgets/stop_text_box.dart rename to lib/ui/widgets/selection_widgets/color_and_stop_selection_widgets/stop_text_box.dart index 626185a..d48007f 100644 --- a/lib/ui/widgets/generator_widgets/selection_widgets/color_and_stop_selection_widgets/stop_text_box.dart +++ b/lib/ui/widgets/selection_widgets/color_and_stop_selection_widgets/stop_text_box.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_gradient_generator/ui/widgets/generator_widgets/selection_widgets/color_and_stop_selection_widgets/outlined_text_field.dart'; +import 'package:flutter_gradient_generator/ui/widgets/selection_widgets/color_and_stop_selection_widgets/outlined_text_field.dart'; import 'package:flutter_gradient_generator/utils/minimum_maximum_integer_input_formatter.dart'; /// A [TextField] that holds the stop value for a color on the Gradient. It only -/// allows integers between [maximumInteger] and [minimumInteger]. +/// allows integers between 0 and 100. +/// +/// See: [_StopTextBoxState.minimumInteger] and [_StopTextBoxState.maximumInteger]. class StopTextBox extends StatefulWidget { const StopTextBox({ super.key, @@ -20,10 +22,9 @@ class StopTextBox extends StatefulWidget { } class _StopTextBoxState extends State { + final minimumInteger = 0; final maximumInteger = 100; - final minumuInteger = 0; - final TextEditingController _controller = TextEditingController(); final FocusNode _focusNode = FocusNode(); @@ -31,7 +32,7 @@ class _StopTextBoxState extends State { List get inputFormatters => [ MinimumMaximumIntegerInputFormatter( - maximumInteger: maximumInteger, minimumInteger: minumuInteger), + maximumInteger: maximumInteger, minimumInteger: minimumInteger), LengthLimitingTextInputFormatter(maximumIntegerLength), ]; @@ -75,7 +76,7 @@ class _StopTextBoxState extends State { Widget build(BuildContext context) { return OutlinedTextField( inputFormatters: inputFormatters, - onSubmitted: onStopSubmitted, + onSubmitted: (_) {}, onTap: () { onTextFieldTap(); }, @@ -97,10 +98,6 @@ class _StopTextBoxState extends State { /// We want to submit the stop value if the text field is focused. /// No need to submit the stop value if the text field is not focused. if (textFieldIsFocused) { - /// Unfocus the text field since the user tapped outside of it and - /// the stop will be submitted. - _focusNode.unfocus(); - /// Get the stop value from the text field. final text = _controller.text; diff --git a/lib/ui/widgets/generator_widgets/selection_widgets/direction_selection_widget.dart b/lib/ui/widgets/selection_widgets/direction_selection_widget.dart similarity index 95% rename from lib/ui/widgets/generator_widgets/selection_widgets/direction_selection_widget.dart rename to lib/ui/widgets/selection_widgets/direction_selection_widget.dart index a858c58..5e31a0d 100644 --- a/lib/ui/widgets/generator_widgets/selection_widgets/direction_selection_widget.dart +++ b/lib/ui/widgets/selection_widgets/direction_selection_widget.dart @@ -5,7 +5,7 @@ import 'package:flutter_gradient_generator/enums/gradient_direction.dart'; import 'package:flutter_gradient_generator/enums/gradient_style.dart'; import 'package:flutter_gradient_generator/view_models/gradient_view_model.dart'; import 'package:flutter_gradient_generator/ui/widgets/buttons/compact_buttons/direction_button.dart'; -import 'package:flutter_gradient_generator/ui/widgets/generator_widgets/selection_container_widget.dart'; +import 'package:flutter_gradient_generator/ui/widgets/selection_widgets/selection_container_widget.dart'; import 'package:flutter_vector_icons/flutter_vector_icons.dart'; import 'package:provider/provider.dart'; @@ -26,6 +26,7 @@ class DirectionSelectionWidget extends StatelessWidget { /// final centerGradientDirectionIndexWithinCenterDirectionSet = 1; + ///TODO: Move this to [DirectionButton] final iconSetList = [ { GradientDirection.topLeft: MaterialCommunityIcons.arrow_top_left, @@ -58,10 +59,7 @@ class DirectionSelectionWidget extends StatelessWidget { final compactButtonMargin = appDimensions.compactButtonMargin; return SelectionWidgetContainer( - titleWidgetInformation: ( - mainTitle: AppStrings.direction, - trailingActionWidget: const SizedBox.shrink(), - ), + title: AppStrings.direction, selectionWidget: Column( children: iconSetList.map( (Map gradientDirectionToIconSetMap) { diff --git a/lib/ui/widgets/selection_widgets/samples_selection_widget.dart b/lib/ui/widgets/selection_widgets/samples_selection_widget.dart new file mode 100644 index 0000000..20fa2ef --- /dev/null +++ b/lib/ui/widgets/selection_widgets/samples_selection_widget.dart @@ -0,0 +1,141 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gradient_generator/data/app_dimensions.dart'; +import 'package:flutter_gradient_generator/data/app_strings.dart'; +import 'package:flutter_gradient_generator/generated/gradient_samples.dart'; +import 'package:flutter_gradient_generator/ui/widgets/selection_widgets/selection_container_widget.dart'; +import 'package:flutter_gradient_generator/utils/analytics.dart'; +import 'package:flutter_gradient_generator/view_models/gradient_view_model.dart'; +import 'package:provider/provider.dart'; + +class SampleSelectionWidget extends StatelessWidget { + const SampleSelectionWidget({ + super.key, + }); + + Widget getSampleItem({ + required BuildContext context, + required GradientViewModel gradientViewModel, + required int index, + required String name, + required Gradient gradient, + }) { + return Column( + children: [ + if (index != 0) + const SizedBox( + height: 6, + ), + Material( + child: InkWell( + onTap: () { + final analytics = context.read(); + + analytics.logGradientSampleClickEvent(name); + + gradientViewModel.setNewFlutterGradient(gradient); + }, + child: Ink( + decoration: BoxDecoration( + gradient: gradient, + ), + height: 48, + child: Center( + child: Text( + name, + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: Colors.white, + shadows: [ + const Shadow( + color: Colors.black54, + offset: Offset(1, 1), + blurRadius: 2, + ), + ], + ), + ), + ), + ), + ), + ), + const SizedBox( + height: 6, + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + final appDimensions = AppDimensions.of(context); + final gradientViewModel = context.watch(); + + final currentGradient = gradientViewModel.gradient; + final currentFlutterGradientConverter = + currentGradient.getFlutterGradientConverter(); + + final samplesListViewSize = appDimensions.samplesListViewSize; + final chooseRandomGradientIconButtonSize = + appDimensions.chooseRandomGradientIconButtonSize; + final sampleTitleBottomMargin = appDimensions.sampleTitleBottomMargin; + + return SelectionWidgetContainer( + title: AppStrings.samples, + selectionWidget: SizedBox( + height: samplesListViewSize, + child: ListView.builder( + prototypeItem: getSampleItem( + context: context, + gradientViewModel: gradientViewModel, + + /// index is set to 1 so that the top margin is added in + /// the prototype item since items with index 0 do not have a top + /// margin. + /// + /// Setting this to 0 would break the layout of the actual items, + /// causing them to overflow by the height of the top margin. + /// + /// See the `getSampleItem` method for more details + index: 1, + name: 'Sample', + gradient: const LinearGradient( + colors: [Colors.black, Colors.white], + ), + ), + shrinkWrap: true, + itemCount: gradientSamples.length, + padding: EdgeInsets.zero, + itemBuilder: (context, index) { + final sample = gradientSamples[index]; + + final name = sample.name; + final colors = sample.colors; + + final gradient = currentFlutterGradientConverter(colors: colors); + + return getSampleItem( + context: context, + gradientViewModel: gradientViewModel, + index: index, + name: name, + gradient: gradient, + ); + }, + ), + ), + titleTrailingWidget: Tooltip( + message: AppStrings.chooseRandomGradient, + child: IconButton( + onPressed: () { + final analytics = context.read(); + + analytics.logRandomGradientSampleButtonClickEvent(); + + gradientViewModel.displayRandomGradientFromSamples(); + }, + icon: const Icon(Icons.shuffle), + iconSize: chooseRandomGradientIconButtonSize), + ), + titleBottomMargin: sampleTitleBottomMargin, + ); + } +} diff --git a/lib/ui/widgets/selection_widgets/selection_container_widget.dart b/lib/ui/widgets/selection_widgets/selection_container_widget.dart new file mode 100644 index 0000000..4f2c987 --- /dev/null +++ b/lib/ui/widgets/selection_widgets/selection_container_widget.dart @@ -0,0 +1,148 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gradient_generator/data/app_dimensions.dart'; + +/// Holds the selection widgets +/// +/// The selection container contains: +/// - a title, +/// - a selection widget, +/// - an optional title trailing widget and +/// - an optional title bottom margin +/// +/// It allows the user to expand and collapse the selection widget +class SelectionWidgetContainer extends StatefulWidget { + const SelectionWidgetContainer({ + super.key, + required this.title, + required this.selectionWidget, + this.titleTrailingWidget, + this.titleBottomMargin = 16.0, + }); + + final String title; + final Widget selectionWidget; + final Widget? titleTrailingWidget; + + /// This helps to adjust the bottom margin of the title for when the title + /// has a trailing widget which might take some extra vertical space. + /// + /// See [ColorAndStopSelectionWidget] for reference. + final double titleBottomMargin; + + @override + State createState() => + _SelectionWidgetContainerState(); +} + +class _SelectionWidgetContainerState extends State + with SingleTickerProviderStateMixin { + static final Animatable _easeInTween = + CurveTween(curve: Curves.easeIn); + static final Animatable _openAndCloseTween = + Tween(begin: -0.25, end: 0); + + bool isExpanded = true; + + late AnimationController _animationController; + late Animation _expansionIconTurns; + late Animation _selectionWidgetOpacity; + late Animation _selectionWidgetSizeFactor; + + @override + void initState() { + super.initState(); + _animationController = AnimationController( + duration: const Duration(milliseconds: 200), vsync: this); + _expansionIconTurns = + _animationController.drive(_openAndCloseTween.chain(_easeInTween)); + _selectionWidgetOpacity = _animationController.drive(_easeInTween); + _selectionWidgetSizeFactor = _animationController.drive(_easeInTween); + + _animationController.value = 1.0; + + _animationController.forward(); + } + + void _toggleExpansion() { + isExpanded = !isExpanded; + + if (isExpanded) { + _animationController.forward(); + } else { + _animationController.reverse(); + } + } + + @override + Widget build(BuildContext context) { + final AppDimensions appDimensions = AppDimensions.of(context); + + final expansionIconSize = appDimensions.expansionIconSize; + + final generatorScreenContentWidth = + appDimensions.generatorScreenContentWidth; + + final generatorScreenHorizontalPadding = + appDimensions.generatorScreenHorizontalPadding; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: EdgeInsets.symmetric( + horizontal: generatorScreenHorizontalPadding, + ), + child: SizedBox( + width: generatorScreenContentWidth, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + RotationTransition( + turns: _expansionIconTurns, + child: GestureDetector( + onTap: _toggleExpansion, + child: Icon( + Icons.arrow_drop_down, + size: expansionIconSize, + color: Colors.black, + ), + ), + ), + const SizedBox( + width: 4.0, + ), + Expanded( + child: Text(widget.title, + style: Theme.of(context).textTheme.labelLarge?.copyWith( + fontWeight: FontWeight.w600, + )), + ), + if (widget.titleTrailingWidget != null) + widget.titleTrailingWidget!, + ], + ), + ), + ), + Padding( + padding: EdgeInsets.symmetric( + horizontal: generatorScreenHorizontalPadding), + child: SizeTransition( + sizeFactor: _selectionWidgetSizeFactor, + child: FadeTransition( + opacity: _selectionWidgetOpacity, + child: Column( + children: [ + SizedBox( + height: widget.titleBottomMargin, + ), + widget.selectionWidget + ], + )), + ), + ), + ], + ); + } +} diff --git a/lib/ui/widgets/generator_widgets/selection_widgets/style_selection_widget.dart b/lib/ui/widgets/selection_widgets/style_selection_widget.dart similarity index 91% rename from lib/ui/widgets/generator_widgets/selection_widgets/style_selection_widget.dart rename to lib/ui/widgets/selection_widgets/style_selection_widget.dart index dce74fc..4964f68 100644 --- a/lib/ui/widgets/generator_widgets/selection_widgets/style_selection_widget.dart +++ b/lib/ui/widgets/selection_widgets/style_selection_widget.dart @@ -5,7 +5,7 @@ import 'package:flutter_gradient_generator/data/app_strings.dart'; import 'package:flutter_gradient_generator/enums/gradient_style.dart'; import 'package:flutter_gradient_generator/view_models/gradient_view_model.dart'; import 'package:flutter_gradient_generator/ui/widgets/buttons/compact_button.dart'; -import 'package:flutter_gradient_generator/ui/widgets/generator_widgets/selection_container_widget.dart'; +import 'package:flutter_gradient_generator/ui/widgets/selection_widgets/selection_container_widget.dart'; import 'package:provider/provider.dart'; class StyleSelectionWidget extends StatelessWidget { @@ -28,10 +28,7 @@ class StyleSelectionWidget extends StatelessWidget { const unselectedStyleButtonColor = Colors.transparent; return SelectionWidgetContainer( - titleWidgetInformation: ( - mainTitle: AppStrings.style, - trailingActionWidget: const SizedBox.shrink() - ), + title: AppStrings.style, selectionWidget: Row( children: GradientStyle.values.map( (GradientStyle style) { diff --git a/lib/utils/analytics.dart b/lib/utils/analytics.dart new file mode 100644 index 0000000..42e7240 --- /dev/null +++ b/lib/utils/analytics.dart @@ -0,0 +1,142 @@ +import 'package:firebase_analytics/firebase_analytics.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_gradient_generator/models/abstract_gradient.dart'; + +///TODO: Set app version when releasing the app + +/// A wrapper class for Analytics event logging. +class Analytics { + /// Logs the [firebaseAnalyticsEvent] and optional [parameters] in release + /// mode only. + /// + /// Ensure to only use this method to log events and not use the Analytics + /// instance (in this case, `FirebaseAnalytics`) directly. + @visibleForTesting + Future logEventInReleaseMode( + FirebaseAnalyticsEvent firebaseAnalyticsEvent, + {Map? parameters}) async { + final FirebaseAnalytics analytics = FirebaseAnalytics.instance; + + /// Return if not in release mode + if (!kReleaseMode) return; + + final firebaseAnalyticsKey = firebaseAnalyticsEvent.key; + + await analytics.logEvent( + name: firebaseAnalyticsKey, parameters: parameters); + } + + /// Logs when a gradient is generated and copied to the clipboard. + Future logGradientGeneratedEvent(AbstractGradient gradient) async { + await logEventInReleaseMode( + FirebaseAnalyticsEvent.gradientGenerated, + parameters: gradient.toJson(), + ); + } + + /// Logs when a gradient is downloaded as an image. + Future logGradientDownloadedAsImageEvent( + AbstractGradient gradient) async { + await logEventInReleaseMode( + FirebaseAnalyticsEvent.gradientDownloadedAsImage, + parameters: gradient.toJson(), + ); + } + + /// Logs when the random gradient sample button is clicked. + Future logRandomGradientSampleButtonClickEvent() async { + await logEventInReleaseMode( + FirebaseAnalyticsEvent.randomGradientSampleButtonClicked, + ); + } + + /// Logs when a gradient sample is clicked. + Future logGradientSampleClickEvent(String sampleName) async { + await logEventInReleaseMode( + FirebaseAnalyticsEvent.gradientSampleClicked, + parameters: { + 'sampleName': sampleName, + }, + ); + } + + /// Logs when the banner ad CTA button is clicked. + Future logBannerAdCTAButtonClickEvent({ + required String name, + required String url, + }) async { + await logEventInReleaseMode( + FirebaseAnalyticsEvent.bannerAdCTAButtonClicked, + parameters: { + 'name': name, + 'url': url, + }, + ); + } + + /// Logs when the banner ad close button is clicked. + Future logBannerAdCloseButtonClickEvent() async { + await logEventInReleaseMode( + FirebaseAnalyticsEvent.bannerAdCloseButtonClicked, + ); + } + + /// Logs when the feature request button is clicked. + Future logFeatureRequestButtonClickEvent() async { + await logEventInReleaseMode( + FirebaseAnalyticsEvent.featureRequestButtonClicked, + ); + } + + /// Logs when the feedback button is clicked. + Future logFeedbackButtonClickEvent() async { + await logEventInReleaseMode( + FirebaseAnalyticsEvent.feedbackButtonClicked, + ); + } + + /// Logs when the bug report button is clicked. + Future logBugReportButtonClickEvent() async { + await logEventInReleaseMode( + FirebaseAnalyticsEvent.bugReportButtonClicked, + ); + } + + /// Logs when the view source code on GitHub button is clicked. + Future logViewSourceCodeOnGitHubButtonClickEvent() async { + await logEventInReleaseMode( + FirebaseAnalyticsEvent.viewSourceCodeOnGitHubButtonClicked, + ); + } + + /// Logs when the Victor Eronmosele link is clicked. + Future logVictorEronmoseleClickEvent() async { + await logEventInReleaseMode( + FirebaseAnalyticsEvent.victorEronmoseleClicked, + ); + } +} + +/// The list of Firebase Analytics events to log. +/// +/// Each event has a unique [key] that is used to log the event. +/// +/// Please use the [key] to log the event and not the enums's `.name` property. +enum FirebaseAnalyticsEvent { + gradientGenerated(key: 'gradientGenerated'), + gradientDownloadedAsImage(key: 'gradientDownloadedAsImage'), + randomGradientSampleButtonClicked(key: 'randomGradientSampleButtonClicked'), + gradientSampleClicked(key: 'gradientSampleClicked'), + bannerAdCTAButtonClicked(key: 'bannerAdCTAButtonClicked'), + bannerAdCloseButtonClicked(key: 'bannerAdCloseButtonClicked'), + featureRequestButtonClicked(key: 'featureRequestButtonClicked'), + feedbackButtonClicked(key: 'feedbackButtonClicked'), + bugReportButtonClicked(key: 'bugReportButtonClicked'), + viewSourceCodeOnGitHubButtonClicked( + key: 'viewSourceCodeOnGitHubButtonClicked'), + victorEronmoseleClicked(key: 'victorEronmoseleClicked'); + + const FirebaseAnalyticsEvent({required this.key}); + + final String key; +} diff --git a/lib/view_models/gradient_view_model.dart b/lib/view_models/gradient_view_model.dart index 5b4d268..a06d53f 100644 --- a/lib/view_models/gradient_view_model.dart +++ b/lib/view_models/gradient_view_model.dart @@ -1,30 +1,31 @@ +import 'dart:math'; + import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gradient_generator/data/app_colors.dart'; import 'package:flutter_gradient_generator/data/app_typedefs.dart'; import 'package:flutter_gradient_generator/enums/gradient_direction.dart'; import 'package:flutter_gradient_generator/enums/gradient_style.dart'; +import 'package:flutter_gradient_generator/generated/gradient_samples.dart'; import 'package:flutter_gradient_generator/models/abstract_gradient.dart'; import 'package:flutter_gradient_generator/models/gradient_factory.dart'; import 'package:flutter_gradient_generator/models/linear_style_gradient.dart'; import 'package:flutter_gradient_generator/ui/util/new_color_generator/new_color_generator.dart'; import 'package:flutter_gradient_generator/ui/util/new_color_generator/new_color_generator_interface.dart'; -import 'package:flutter_gradient_generator/ui/util/random_color_generator/abstract_random_color_generator.dart'; -import 'package:flutter_gradient_generator/ui/util/random_color_generator/random_color_generator.dart'; import 'package:flutter_gradient_generator/utils/color_and_stop_util.dart'; class GradientViewModel with ChangeNotifier { - final AbstractRandomColorGenerator _randomColorGenerator = - const RandomColorGenerator(); final _colorAndStopUtil = ColorAndStopUtil(); - late final AbstractGradient _defaultGradient = LinearStyleGradient( + /// The default gradient shown when the app is first opened. + late final AbstractGradient defaultGradient = LinearStyleGradient( colorAndStopList: AppColors.initialColorAndStopList, gradientDirection: GradientDirection.topLeft); final NewColorGeneratorInterface _newColorGenerator = NewColorGenerator(); - late AbstractGradient gradient = _defaultGradient; + AbstractGradient get gradient => _gradient; + late AbstractGradient _gradient = defaultGradient; /// Whether the color change is from the [HtmlColorInput] widget. /// @@ -35,7 +36,7 @@ class GradientViewModel with ChangeNotifier { bool changeIsFromHtmlColorInput = false; void addNewColor() { - final lastColorAndStop = gradient.getColorAndStopList().last; + final lastColorAndStop = _gradient.getColorAndStopList().last; final newColorAndStop = _newColorGenerator.generateNewColorAndStop( seedColorAndStop: lastColorAndStop, ); @@ -47,7 +48,7 @@ class GradientViewModel with ChangeNotifier { required Color newColor, required int currentColorAndStopIndex, }) { - final colorAndStopList = gradient.getColorAndStopList(); + final colorAndStopList = _gradient.getColorAndStopList(); // ignore: unused_local_variable final (color: _, stop: stop) = @@ -72,9 +73,8 @@ class GradientViewModel with ChangeNotifier { required int newStop, required int currentColorAndStopIndex, }) { - final colorAndStopList = gradient.getColorAndStopList(); + final colorAndStopList = _gradient.getColorAndStopList(); - // ignore: unused_local_variable final (color: color, stop: _) = colorAndStopList.elementAt(currentColorAndStopIndex); @@ -97,7 +97,7 @@ class GradientViewModel with ChangeNotifier { /// [minimumNumberOfColors] colors in the gradient. void deleteSelectedColorAndStopIfMoreThanMinimum( {required int indexToDelete}) { - final currentColorAndStopList = gradient.getColorAndStopList(); + final currentColorAndStopList = _gradient.getColorAndStopList(); final colorAndStopListIsMoreThanMinimum = _colorAndStopUtil .isColorAndStopListMoreThanMinimum(currentColorAndStopList); @@ -112,36 +112,56 @@ class GradientViewModel with ChangeNotifier { } void changeGradientDirection(GradientDirection newGradientDirection) { - if (gradient.getGradientDirection() != newGradientDirection) { + if (_gradient.getGradientDirection() != newGradientDirection) { final AbstractGradient newGradient = GradientFactory().getGradient( - gradientStyle: gradient.getGradientStyle(), - colorAndStopList: gradient.getColorAndStopList(), + gradientStyle: _gradient.getGradientStyle(), + colorAndStopList: _gradient.getColorAndStopList(), gradientDirection: newGradientDirection, ); - gradient = newGradient; + _gradient = newGradient; notifyListeners(); } } void changeGradientStyle(GradientStyle newGradientStyle) { - if (gradient.getGradientStyle() != newGradientStyle) { + if (_gradient.getGradientStyle() != newGradientStyle) { final AbstractGradient newGradient = GradientFactory().getGradient( gradientStyle: newGradientStyle, - colorAndStopList: gradient.getColorAndStopList(), - gradientDirection: gradient.getGradientDirection(), + colorAndStopList: _gradient.getColorAndStopList(), + gradientDirection: _gradient.getGradientDirection(), ); - gradient = newGradient; + _gradient = newGradient; notifyListeners(); } } + void setNewFlutterGradient(Gradient newFlutterGradient) { + final colorCount = newFlutterGradient.colors.length; + + final colorAndStopList = newFlutterGradient.colors.mapIndexed( + (index, color) { + final stop = ((100 * index) / (colorCount - 1)).ceil(); + + return (color: color, stop: stop); + }, + ); + + _gradient = GradientFactory().getGradient( + gradientStyle: _gradient.getGradientStyle(), + colorAndStopList: colorAndStopList.toList(), + gradientDirection: _gradient.getGradientDirection(), + ); + + notifyListeners(); + } + void onNewColorAndStopAdded(ColorAndStop newColorAndStop) { final List colorAndStopListCopy = - List.from(gradient.getColorAndStopList()); + List.from(_gradient.getColorAndStopList()); colorAndStopListCopy.add(newColorAndStop); @@ -153,22 +173,24 @@ class GradientViewModel with ChangeNotifier { ); } - void randomizeColors() { - final newColorAndStopList = _randomColorGenerator - .getRandomColorAndStopsOfCurrentGradientColorAndStopListLength( - currentStopList: - gradient.getColorAndStopList().map((e) => e.stop).toList(), - ); + void displayRandomGradientFromSamples() { + final Random random = Random(); - _onColorAndStopListChanged( - newColorAndStopList, - isChangeFromHtmlColorInput: false, + final randomGradientFromSamples = + gradientSamples[random.nextInt(gradientSamples.length)]; + + final flutterGradientConverter = gradient.getFlutterGradientConverter(); + + final randomGradient = flutterGradientConverter( + colors: randomGradientFromSamples.colors, ); + + setNewFlutterGradient(randomGradient); } void _onColorAndStopDeleted(ColorAndStop colorAndStopToDelete) { final List colorAndStopListCopy = - List.from(gradient.getColorAndStopList()); + List.from(_gradient.getColorAndStopList()); colorAndStopListCopy.remove(colorAndStopToDelete); @@ -183,14 +205,14 @@ class GradientViewModel with ChangeNotifier { void _onColorAndStopListChanged(List newColorAndStopList, {required bool isChangeFromHtmlColorInput}) { if (!const ListEquality() - .equals(gradient.getColorAndStopList(), newColorAndStopList)) { + .equals(_gradient.getColorAndStopList(), newColorAndStopList)) { final AbstractGradient newGradient = GradientFactory().getGradient( - gradientStyle: gradient.getGradientStyle(), + gradientStyle: _gradient.getGradientStyle(), colorAndStopList: newColorAndStopList, - gradientDirection: gradient.getGradientDirection(), + gradientDirection: _gradient.getGradientDirection(), ); - gradient = newGradient; + _gradient = newGradient; changeIsFromHtmlColorInput = isChangeFromHtmlColorInput; diff --git a/pubspec.lock b/pubspec.lock index fbd93f1..811f3ea 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,34 +5,34 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "98d1d33ed129b372846e862de23a0fc365745f4d7b5e786ce667fcbbb7ac5c07" + sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 url: "https://pub.dev" source: hosted - version: "55.0.0" + version: "64.0.0" _flutterfire_internals: dependency: transitive description: name: _flutterfire_internals - sha256: cb3a948a1eebbf8efd987c43f95418269930e912a88bc7b6a5a7658423133635 + sha256: "1a52f1afae8ab7ac4741425114713bdbba802f1ce1e0648e167ffcc6e05e96cf" url: "https://pub.dev" source: hosted - version: "1.0.17" + version: "1.3.21" analyzer: dependency: transitive description: name: analyzer - sha256: "881348aed9b0b425882c97732629a6a31093c8ff20fc4b3b03fb9d3d50a3a126" + sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" url: "https://pub.dev" source: hosted - version: "5.7.1" + version: "6.2.0" args: dependency: transitive description: name: args - sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440" + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.2" async: dependency: transitive description: @@ -57,6 +57,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + charcode: + dependency: transitive + description: + name: charcode + sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 + url: "https://pub.dev" + source: hosted + version: "1.3.1" clock: dependency: transitive description: @@ -69,10 +77,10 @@ packages: dependency: "direct main" description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" convert: dependency: transitive description: @@ -85,18 +93,26 @@ packages: dependency: transitive description: name: coverage - sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097" + sha256: "8acabb8306b57a409bf4c83522065672ee13179297a6bb0cb9ead73948df7c76" url: "https://pub.dev" source: hosted - version: "1.6.3" + version: "1.7.2" crypto: dependency: transitive description: name: crypto - sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" + csslib: + dependency: transitive + description: + name: csslib + sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" + url: "https://pub.dev" + source: hosted + version: "1.0.0" fake_async: dependency: transitive description: @@ -109,66 +125,74 @@ packages: dependency: transitive description: name: ffi - sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" file: dependency: transitive description: name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "7.0.0" firebase_analytics: dependency: "direct main" description: name: firebase_analytics - sha256: "9972d5b57188adbbd8d112cb3076ac3c6abd677cf9286783cc5755a3d472e7ec" + sha256: edb9f9eaecf0e6431e5c12b7fabdb68be3e85ce51f941ccbfa6cb71327e8b535 url: "https://pub.dev" source: hosted - version: "10.1.5" + version: "10.8.5" firebase_analytics_platform_interface: dependency: transitive description: name: firebase_analytics_platform_interface - sha256: b8f49e80e78d732aad7298600b6fb1512890632cd677cb4dcf1ac773fb7fa0ee + sha256: de4a54353cf58412c6da6b660a0dbad8efacb33b345c0286bc3a2edb869124d8 url: "https://pub.dev" source: hosted - version: "3.3.22" + version: "3.9.5" firebase_analytics_web: dependency: transitive description: name: firebase_analytics_web - sha256: "34f6d9c0927c33f27e883da466456801ee0bf5a0e28aa60fe2c4160a1767bccd" + sha256: "77e4c02ffd0204ccc7856221193265c807b7e056fa62855f973a7f77435b5d41" url: "https://pub.dev" source: hosted - version: "0.5.1+13" + version: "0.5.5+17" firebase_core: dependency: "direct main" description: name: firebase_core - sha256: "1c121a478af23755b0b93fd4aa70d3bd32a587dd51ef0a3979091ac0d2317d32" + sha256: "7e049e32a9d347616edb39542cf92cd53fdb4a99fb6af0a0bff327c14cd76445" url: "https://pub.dev" source: hosted - version: "2.7.1" + version: "2.25.4" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - sha256: "5615b30c36f55b2777d0533771deda7e5730e769e5d3cb7fda79e9bed86cfa55" + sha256: c437ae5d17e6b5cc7981cf6fd458a5db4d12979905f9aafd1fea930428a9fe63 url: "https://pub.dev" source: hosted - version: "4.5.3" + version: "5.0.0" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: "0c1cf1f1022d2245ac117443bb95207952ca770281524d2908e323bc063fb8ff" + sha256: "57e61d6010e253b36d38191cefd6199d7849152cdcd234b61ca290cdb278a0ba" url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.11.4" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" flutter: dependency: "direct main" description: flutter @@ -178,10 +202,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.3" flutter_test: dependency: "direct dev" description: flutter @@ -190,11 +214,12 @@ packages: flutter_vector_icons: dependency: "direct main" description: - name: flutter_vector_icons - sha256: "20a77e282f8cb438c8edcee4eec85d8497d5f4cbeb07d116e9d2d01c9edfc5e2" - url: "https://pub.dev" - source: hosted - version: "2.0.0" + path: "." + ref: "v3.0.0" + resolved-ref: "364f724521c2dc580ee66c1e653329a2bac38e89" + url: "https://github.com/victoreronmosele/flutter-vector-icons.git" + source: git + version: "3.0.0" flutter_web_plugins: dependency: transitive description: flutter @@ -212,35 +237,43 @@ packages: dependency: transitive description: name: glob - sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c" + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" google_fonts: dependency: "direct main" description: name: google_fonts - sha256: "927573f2e8a8d65c17931e21918ad0ab0666b1b636537de7c4932bdb487b190f" + sha256: "2776c66b3e97c6cdd58d1bd3281548b074b64f1fd5c8f82391f7456e38849567" + url: "https://pub.dev" + source: hosted + version: "4.0.5" + html: + dependency: transitive + description: + name: html + sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" url: "https://pub.dev" source: hosted - version: "4.0.3" + version: "0.15.4" html_color_input: dependency: "direct main" description: path: "." ref: "feature/implement-html-color-input" - resolved-ref: "2a0b26d9c46283f82d5482a246202b4e2c906d6b" + resolved-ref: "68943e86c867e97baf9dca270301bdd7fc743d47" url: "https://github.com/victoreronmosele/flutter_html_color_input.git" source: git - version: "0.0.1" + version: "1.0.0" http: dependency: transitive description: name: http - sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba url: "https://pub.dev" source: hosted - version: "0.13.5" + version: "1.2.0" http_multi_server: dependency: transitive description: @@ -257,6 +290,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + image_downloader_web: + dependency: "direct main" + description: + name: image_downloader_web + sha256: b7fe34460541eedf8f7f616bca25d07068c19eddee0e409b6cba90fa39fee984 + url: "https://pub.dev" + source: hosted + version: "2.0.4" io: dependency: transitive description: @@ -277,18 +318,18 @@ packages: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.1" logging: dependency: transitive description: name: logging - sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d" + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.2.0" matcher: dependency: transitive description: @@ -306,21 +347,29 @@ packages: source: hosted version: "0.5.0" meta: - dependency: transitive + dependency: "direct main" description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" mime: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" + mocktail: + dependency: transitive + description: + name: mocktail + sha256: c4b5007d91ca4f67256e720cb1b6d704e79a510183a12fa551021f652577dce6 + url: "https://pub.dev" + source: hosted + version: "1.0.3" nested: dependency: transitive description: @@ -357,50 +406,50 @@ packages: dependency: transitive description: name: path_provider - sha256: "04890b994ee89bfa80bf3080bfec40d5a92c5c7a785ebb02c13084a099d2b6f9" + sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b url: "https://pub.dev" source: hosted - version: "2.0.13" + version: "2.1.2" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "019f18c9c10ae370b08dce1f3e3b73bc9f58e7f087bb5e921f06529438ac0ae7" + sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" url: "https://pub.dev" source: hosted - version: "2.0.24" + version: "2.2.2" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "026b97a6c29da75181a37aae2eba9227f5fe13cb2838c6b975ce209328b8ab4e" + sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.3.2" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1" + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 url: "https://pub.dev" source: hosted - version: "2.1.10" + version: "2.2.1" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.1.2" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: f53720498d5a543f9607db4b0e997c4b5438884de25b0f73098cc2671a51b130 + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.2.1" pigment: dependency: transitive description: @@ -413,18 +462,18 @@ packages: dependency: transitive description: name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.4" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.8" pool: dependency: transitive description: @@ -433,30 +482,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" - process: - dependency: transitive - description: - name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" - url: "https://pub.dev" - source: hosted - version: "4.2.4" provider: dependency: "direct main" description: name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" url: "https://pub.dev" source: hosted - version: "6.0.5" + version: "6.1.1" pub_semver: dependency: transitive description: name: pub_semver - sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" quiver: dependency: "direct main" description: @@ -465,38 +506,46 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.1" + screenshot: + dependency: "direct main" + description: + name: screenshot + sha256: "455284ff1f5b911d94a43c25e1385485cf6b4f288293eba68f15dad711c7b81c" + url: "https://pub.dev" + source: hosted + version: "2.1.0" shelf: dependency: transitive description: name: shelf - sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" shelf_packages_handler: dependency: transitive description: name: shelf_packages_handler - sha256: aef74dc9195746a384843102142ab65b6a4735bb3beea791e63527b88cc83306 + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" shelf_static: dependency: transitive description: name: shelf_static - sha256: e792b76b96a36d4a41b819da593aff4bdd413576b3ba6150df5d8d9996d2e74c + sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8 + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" sky_engine: dependency: transitive description: flutter @@ -538,18 +587,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -570,26 +619,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" + sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f url: "https://pub.dev" source: hosted - version: "1.24.3" + version: "1.24.9" test_api: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" test_core: dependency: transitive description: name: test_core - sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" + sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a url: "https://pub.dev" source: hosted - version: "0.5.3" + version: "0.5.9" tinycolor2: dependency: "direct main" description: @@ -602,74 +651,90 @@ packages: dependency: transitive description: name: typed_data - sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" + universal_html: + dependency: transitive + description: + name: universal_html + sha256: "56536254004e24d9d8cfdb7dbbf09b74cf8df96729f38a2f5c238163e3d58971" + url: "https://pub.dev" + source: hosted + version: "2.2.4" + universal_io: + dependency: transitive + description: + name: universal_io + sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" + url: "https://pub.dev" + source: hosted + version: "2.2.2" url_launcher: dependency: "direct main" description: name: url_launcher - sha256: "75f2846facd11168d007529d6cd8fcb2b750186bea046af9711f10b907e1587e" + sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c url: "https://pub.dev" source: hosted - version: "6.1.10" + version: "6.2.4" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "845530e5e05db5500c1a4c1446785d60cbd8f9bd45e21e7dd643a3273bb4bbd1" + sha256: "507dc655b1d9cb5ebc756032eb785f114e415f91557b73bf60b7e201dfedeb2f" url: "https://pub.dev" source: hosted - version: "6.0.25" + version: "6.2.2" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "7ab1e5b646623d6a2537aa59d5d039f90eebef75a7c25e105f6f75de1f7750c3" + sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03" url: "https://pub.dev" source: hosted - version: "6.1.2" + version: "6.2.4" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: "206fb8334a700ef7754d6a9ed119e7349bc830448098f21a69bf1b4ed038cabc" + sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 url: "https://pub.dev" source: hosted - version: "3.0.4" + version: "3.1.1" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: "0ef2b4f97942a16523e51256b799e9aa1843da6c60c55eefbfa9dbc2dcb8331a" + sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 url: "https://pub.dev" source: hosted - version: "3.0.4" + version: "3.1.0" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370" + sha256: a932c3a8082e118f80a475ce692fde89dc20fddb24c57360b96bc56f7035de1f url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.3.1" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "81fe91b6c4f84f222d186a9d23c73157dc4c8e1c71489c4d08be1ad3b228f1aa" + sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b url: "https://pub.dev" source: hosted - version: "2.0.16" + version: "2.2.3" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: a83ba3607a507758669cfafb03f9de09bf6e6280c14d9b9cb18f013e406dcacd + sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.1.1" url_strategy: dependency: "direct main" description: @@ -682,10 +747,10 @@ packages: dependency: transitive description: name: uuid - sha256: b715b8d3858b6fa9f68f87d20d98830283628014750c2b09b6f516c1da4af2a7 + sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "4.3.3" vector_math: dependency: transitive description: @@ -698,66 +763,66 @@ packages: dependency: transitive description: name: vm_service - sha256: e7fb6c2282f7631712b69c19d1bff82f3767eea33a2321c14fa59ad67ea391c7 + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "9.4.0" + version: "13.0.0" watcher: dependency: transitive description: name: watcher - sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.1.0" web: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.4.0" webkit_inspection_protocol: dependency: transitive description: name: webkit_inspection_protocol - sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d" + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.1" win32: dependency: transitive description: name: win32 - sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46 + sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "5.2.0" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1 + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.4" yaml: dependency: transitive description: name: yaml - sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.2" sdks: - dart: ">=3.1.3 <4.0.0" - flutter: ">=3.13.6" + dart: ">=3.2.6 <4.0.0" + flutter: ">=3.16.9" diff --git a/pubspec.yaml b/pubspec.yaml index 9786558..69ee474 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_gradient_generator description: A new Flutter project. - +homepage: fluttergradientgenerator.com # The following line prevents the package from being accidentally published to # pub.dev using `pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev @@ -18,13 +18,18 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=3.1.0 <4.0.0" - flutter: ">=3.13.6" + sdk: ">=3.2.6 <4.0.0" + flutter: ">=3.16.9" dependencies: flutter: sdk: flutter - flutter_vector_icons: ^2.0.0 + # The original package, https://pub.dev/packages/flutter_vector_icons, does not support + # icon tree shaking. This is my fork of the original package with fixes for icon tree shaking. + flutter_vector_icons: + git: + url: https://github.com/victoreronmosele/flutter-vector-icons.git + ref: v3.0.0 collection: ^1.15.0 url_strategy: ^0.2.0 quiver: ^3.0.1+1 @@ -38,6 +43,9 @@ dependencies: url: https://github.com/victoreronmosele/flutter_html_color_input.git ref: feature/implement-html-color-input tinycolor2: ^3.0.1 + meta: ^1.10.0 + screenshot: ^2.1.0 + image_downloader_web: ^2.0.4 dev_dependencies: @@ -57,33 +65,3 @@ flutter: # the material Icons class. uses-material-design: true - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages diff --git a/screenshots/screenshot.png b/screenshots/screenshot.png index 864c03a..05292d7 100644 Binary files a/screenshots/screenshot.png and b/screenshots/screenshot.png differ diff --git a/shuffle.svg b/shuffle.svg new file mode 100644 index 0000000..4a31179 --- /dev/null +++ b/shuffle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/unit_test/matchers.dart b/test/unit_test/matchers.dart new file mode 100644 index 0000000..1538d2c --- /dev/null +++ b/test/unit_test/matchers.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gradient_generator/data/app_typedefs.dart'; +import 'package:test/test.dart'; + +Matcher flutterGradientConverterEquals( + FlutterGradientConverter expected, { + required List colors, + List? stops, +}) => + _FlutterGradientConverterEquals(expected, colors: colors, stops: stops); + +class _FlutterGradientConverterEquals extends Matcher { + const _FlutterGradientConverterEquals(this.expected, + {required this.colors, this.stops}); + + final FlutterGradientConverter expected; + final List colors; + final List? stops; + + @override + bool matches(Object? actual, Map matchState) { + if (actual is FlutterGradientConverter) { + return actual.call(colors: colors, stops: stops) == + expected.call(colors: colors, stops: stops); + } + return false; + } + + @override + Description describe(Description description) => + description.addDescriptionOf(expected); +} diff --git a/test/unit_test/models/gradient_factory_test.dart b/test/unit_test/models/gradient_factory_test.dart index 110b23b..38cdfd3 100644 --- a/test/unit_test/models/gradient_factory_test.dart +++ b/test/unit_test/models/gradient_factory_test.dart @@ -6,39 +6,69 @@ import 'package:flutter_gradient_generator/models/abstract_gradient.dart'; import 'package:flutter_gradient_generator/models/gradient_factory.dart'; import 'package:flutter_gradient_generator/models/linear_style_gradient.dart'; import 'package:flutter_gradient_generator/models/radial_style_gradient.dart'; +import 'package:flutter_gradient_generator/models/sweep_style_gradient.dart'; import 'package:test/test.dart'; void main() { - test( - 'GradientFactory returns the right AbstractGradient object given a GradientStyle', - () { - final List colorAndStopList = [ - (color: const Color(0xFF921E1E), stop: 0), - (color: const Color(0xFF0A951F), stop: 100), - ]; - - const GradientDirection gradientDirection = GradientDirection.topLeft; - - final Map - gradientStyleToAbstractGradientMap = { - GradientStyle.linear: LinearStyleGradient( - colorAndStopList: colorAndStopList, - gradientDirection: gradientDirection), - GradientStyle.radial: RadialStyleGradient( - colorAndStopList: colorAndStopList, - gradientDirection: gradientDirection) - }; - - gradientStyleToAbstractGradientMap.forEach( - (GradientStyle gradientStyle, AbstractGradient abstractGradient) { + group('GradientFactory ', () { + test('returns the right AbstractGradient object given a GradientStyle', () { + final List colorAndStopList = [ + (color: const Color(0xFF921E1E), stop: 0), + (color: const Color(0xFF0A951F), stop: 100), + ]; + + const GradientDirection gradientDirection = GradientDirection.topLeft; + + final Map + gradientStyleToAbstractGradientMap = { + GradientStyle.linear: LinearStyleGradient( + colorAndStopList: colorAndStopList, + gradientDirection: gradientDirection), + GradientStyle.radial: RadialStyleGradient( + colorAndStopList: colorAndStopList, + gradientDirection: gradientDirection), + GradientStyle.sweep: SweepStyleGradient( + colorAndStopList: colorAndStopList, + gradientDirection: gradientDirection), + }; + + gradientStyleToAbstractGradientMap.forEach( + (GradientStyle gradientStyle, AbstractGradient abstractGradient) { + final AbstractGradient actualAbstractGradient = GradientFactory() + .getGradient( + gradientStyle: gradientStyle, + colorAndStopList: colorAndStopList, + gradientDirection: gradientDirection); + + final AbstractGradient expectedAbstractGradient = abstractGradient; + + expect(actualAbstractGradient, expectedAbstractGradient); + }); + }); + + test('sorts the ColorAndStop list by the stop value', () { + final List unsortedColorAndStopList = [ + (color: const Color(0xFF921E1E), stop: 100), + (color: const Color(0xFF0A951F), stop: 0), + (color: const Color(0xFF0022FF), stop: 50), + ]; + final AbstractGradient actualAbstractGradient = GradientFactory() .getGradient( - gradientStyle: gradientStyle, - colorAndStopList: colorAndStopList, - gradientDirection: gradientDirection); - final AbstractGradient expectedAbstractGradient = abstractGradient; + gradientStyle: GradientStyle.linear, + colorAndStopList: unsortedColorAndStopList, + gradientDirection: GradientDirection.topLeft); + + final actualColorAndStopList = + actualAbstractGradient.getColorAndStopList(); + + final List expectedColorAndStopList = [ + (color: const Color(0xFF0A951F), stop: 0), + (color: const Color(0xFF0022FF), stop: 50), + (color: const Color(0xFF921E1E), stop: 100), + ]; - expect(actualAbstractGradient, expectedAbstractGradient); + expect(actualColorAndStopList, expectedColorAndStopList); }); }); } diff --git a/test/unit_test/models/linear_style_gradient_test.dart b/test/unit_test/models/linear_style_gradient_test.dart index f788edc..61e9d1d 100644 --- a/test/unit_test/models/linear_style_gradient_test.dart +++ b/test/unit_test/models/linear_style_gradient_test.dart @@ -5,6 +5,8 @@ import 'package:flutter_gradient_generator/enums/gradient_style.dart'; import 'package:flutter_gradient_generator/models/linear_style_gradient.dart'; import 'package:test/test.dart'; +import '../matchers.dart'; + void main() { group('LinearStyleGradient', () { late final List colorAndStopList; @@ -98,19 +100,36 @@ void main() { expect(actualGradientStyle, expectedGradientStyle); }); - test('.toFlutterGradient() returns the right LinearGradient object', () { - final Gradient actualGradient = linearStyleGradient.toFlutterGradient(); - final Gradient expectedGradient = LinearGradient( - colors: colorAndStopList - .map((colorAndStop) => colorAndStop.color) - .toList(), - begin: Alignment.topLeft, - end: Alignment.bottomRight, - stops: colorAndStopList - .map((colorAndStop) => colorAndStop.stop / 100) - .toList()); - - expect(actualGradient, expectedGradient); + test( + '.getFlutterGradientConverter() returns the right FlutterGradientConverter object', + () { + final actualFlutterGradientConverter = + linearStyleGradient.getFlutterGradientConverter(); + // ignore: prefer_function_declarations_over_variables + final expectedFlutterGradientConverter = ( + {required List colors, List? stops}) => + LinearGradient( + colors: colors, + begin: Alignment.topLeft, + end: Alignment.bottomRight, + stops: stops, + ); + + final colorsForValidation = + colorAndStopList.map((colorAndStop) => colorAndStop.color).toList(); + + final stopsForValidation = colorAndStopList + .map((colorAndStop) => colorAndStop.stop / 100) + .toList(); + + expect( + actualFlutterGradientConverter, + flutterGradientConverterEquals( + expectedFlutterGradientConverter, + colors: colorsForValidation, + stops: stopsForValidation, + ), + ); }); }); } diff --git a/test/unit_test/models/radial_style_gradient_test.dart b/test/unit_test/models/radial_style_gradient_test.dart index 99240ea..1c6d353 100644 --- a/test/unit_test/models/radial_style_gradient_test.dart +++ b/test/unit_test/models/radial_style_gradient_test.dart @@ -5,6 +5,8 @@ import 'package:flutter_gradient_generator/enums/gradient_style.dart'; import 'package:flutter_gradient_generator/models/radial_style_gradient.dart'; import 'package:test/test.dart'; +import '../matchers.dart'; + void main() { group('RadialStyleGradient', () { late final List colorAndStopList; @@ -70,18 +72,35 @@ void main() { expect(actualGradientStyle, expectedGradientStyle); }); - test('.toFlutterGradient() returns the right RadialGradient object', () { - final Gradient actualGradient = radialStyleGradient.toFlutterGradient(); - final Gradient expectedGradient = RadialGradient( - colors: - colorAndStopList.map((colorAndStop) => colorAndStop.color).toList(), - center: Alignment.topLeft, - stops: colorAndStopList - .map((colorAndStop) => colorAndStop.stop / 100) - .toList(), - ); - - expect(actualGradient, expectedGradient); + test( + 'getFlutterGradientConverter() returns the right FlutterGradientConverter', + () { + final actualFlutterGradientConverter = + radialStyleGradient.getFlutterGradientConverter(); + + // ignore: prefer_function_declarations_over_variables + final expectedFlutterGradientConverter = ( + {required List colors, List? stops}) => + RadialGradient( + colors: colors, + stops: stops, + center: Alignment.topLeft, + ); + + final colorsForValidation = + colorAndStopList.map((colorAndStop) => colorAndStop.color).toList(); + + final stopsForValidation = colorAndStopList + .map((colorAndStop) => colorAndStop.stop / 100) + .toList(); + + expect( + actualFlutterGradientConverter, + flutterGradientConverterEquals( + expectedFlutterGradientConverter, + colors: colorsForValidation, + stops: stopsForValidation, + )); }); }); } diff --git a/test/unit_test/models/sweep_style_gradient_test.dart b/test/unit_test/models/sweep_style_gradient_test.dart index fb3867a..ea53ed0 100644 --- a/test/unit_test/models/sweep_style_gradient_test.dart +++ b/test/unit_test/models/sweep_style_gradient_test.dart @@ -5,6 +5,8 @@ import 'package:flutter_gradient_generator/enums/gradient_style.dart'; import 'package:flutter_gradient_generator/models/sweep_style_gradient.dart'; import 'package:test/test.dart'; +import '../matchers.dart'; + void main() { group('SweepStyleGradient', () { late final List colorAndStopList; @@ -70,19 +72,31 @@ void main() { expect(actualGradientStyle, expectedGradientStyle); }); - test('.toFlutterGradient() returns the right SweepGradient object', () { - final Gradient actualSweepGradient = - sweepStyleGradient.toFlutterGradient(); - final Gradient expectedSweepGradient = SweepGradient( - colors: - colorAndStopList.map((colorAndStop) => colorAndStop.color).toList(), - center: Alignment.topLeft, - stops: colorAndStopList - .map((colorAndStop) => colorAndStop.stop / 100) - .toList(), - ); + test( + '.getFlutterGradientConverter() returns the right FlutterGradientConverter object', + () { + final FlutterGradientConverter actualFlutterGradientConverter = + sweepStyleGradient.getFlutterGradientConverter(); + // ignore: prefer_function_declarations_over_variables + final FlutterGradientConverter expectedFlutterGradientConverter = + ({required List colors, List? stops}) => SweepGradient( + colors: colors, + center: Alignment.topLeft, + stops: stops, + ); + + final colorsForValidation = + colorAndStopList.map((colorAndStop) => colorAndStop.color).toList(); - expect(actualSweepGradient, expectedSweepGradient); + final stopsForValidation = colorAndStopList + .map((colorAndStop) => colorAndStop.stop / 100) + .toList(); + + expect( + actualFlutterGradientConverter, + flutterGradientConverterEquals(expectedFlutterGradientConverter, + colors: colorsForValidation, stops: stopsForValidation), + ); }); }); } diff --git a/tool/.gitignore b/tool/.gitignore new file mode 100644 index 0000000..3a85790 --- /dev/null +++ b/tool/.gitignore @@ -0,0 +1,3 @@ +# https://dart.dev/guides/libraries/private-files +# Created by `dart pub` +.dart_tool/ diff --git a/tool/CHANGELOG.md b/tool/CHANGELOG.md new file mode 100644 index 0000000..effe43c --- /dev/null +++ b/tool/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +- Initial version. diff --git a/tool/README.md b/tool/README.md new file mode 100644 index 0000000..f613be7 --- /dev/null +++ b/tool/README.md @@ -0,0 +1,25 @@ +# Flutter Gradient Generator Tool + +A command-line application providing tools that assist with development of Flutter Gradient Generator. + +## Current tools: +- ### Gradient Samples Generator: + + This generates a Dart file containing a list of gradient samples used in the Flutter Gradient Generator. + + - The gradient samples are generated from the `gradients.json` file located in the `assets` directory. + + - The generated file is located in the `Flutter Gradient Generator` project at `lib/generated/gradient_samples.dart`. + + + #### Usage: + Run the following command: + ```dart + dart bin/generate_gradient_samples.dart + ``` + +## Testing: +The tool can be tested by running the following command: +```dart +flutter test +``` \ No newline at end of file diff --git a/tool/analysis_options.yaml b/tool/analysis_options.yaml new file mode 100644 index 0000000..dee8927 --- /dev/null +++ b/tool/analysis_options.yaml @@ -0,0 +1,30 @@ +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml + +# Uncomment the following section to specify additional rules. + +# linter: +# rules: +# - camel_case_types + +# analyzer: +# exclude: +# - path/to/excluded/files/** + +# For more information about the core and recommended set of lints, see +# https://dart.dev/go/core-lints + +# For additional information about configuring this file, see +# https://dart.dev/guides/language/analysis-options diff --git a/tool/assets/gradients.json b/tool/assets/gradients.json new file mode 100644 index 0000000..60d987c --- /dev/null +++ b/tool/assets/gradients.json @@ -0,0 +1,1536 @@ +[ + { + "name": "Omolon", + "colors": ["#091E3A", "#2F80ED", "#2D9EE0"] + }, + { + "name": "Farhan", + "colors": ["#9400D3", "#4B0082"] + }, + { + "name": "Purple", + "colors": ["#c84e89", "#F15F79"] + }, + { + "name": "Ibtesam", + "colors": ["#00F5A0", "#00D9F5"] + }, + { + "name": "Radioactive Heat", + "colors": ["#F7941E", "#72C6EF", "#00A651"] + }, + { + "name": "The Sky And The Sea", + "colors": ["#F7941E", "#004E8F"] + }, + { + "name": "From Ice To Fire", + "colors": ["#72C6EF", "#004E8F"] + }, + + { + "name": "Blue & Orange", + "colors": ["#FD8112", "#0085CA"] + }, + { + "name": "Purple Dream", + "colors": ["#bf5ae0","#a811da"] + }, + { + "name": "Blu", + "colors": ["#00416A", "#E4E5E6"] + }, + { + "name": "Summer Breeze", + "colors": ["#fbed96", "#abecd6"] + }, + { + "name": "Ver", + "colors": ["#FFE000", "#799F0C"] + }, + { + "name": "Ver Black", + "colors": ["#F7F8F8", "#ACBB78"] + }, + { + "name": "Combi", + "colors": ["#00416A", "#799F0C", "#FFE000"] + }, + { + "name": "Anwar", + "colors": ["#334d50", "#cbcaa5"] + }, + { + "name": "Bluelagoo", + "colors": ["#0052D4", "#4364F7", "#6FB1FC"] + }, + { + "name": "Lunada", + "colors": ["#5433FF", "#20BDFF", "#A5FECB"] + }, + { + "name": "Reaqua", + "colors": ["#799F0C", "#ACBB78"] + }, + { + "name": "Mango", + "colors": ["#ffe259", "#ffa751"] + }, + { + "name": "Bupe", + "colors": ["#00416A", "#E4E5E6"] + }, + { + "name": "Rea", + "colors": ["#FFE000", "#799F0C"] + }, + { + "name": "Windy", + "colors": ["#acb6e5", "#86fde8"] + }, + { + "name": "Royal Blue", + "colors": ["#536976", "#292E49"] + }, + { + "name": "Royal Blue + Petrol", + "colors": ["#BBD2C5", "#536976", "#292E49"] + }, + { + "name": "Copper", + "colors": ["#B79891", "#94716B"] + }, + { + "name": "Anamnisar", + "colors": ["#9796f0", "#fbc7d4"] + }, + { + "name": "Petrol", + "colors": ["#BBD2C5", "#536976"] + }, + { + "name": "Sky", + "colors": ["#076585", "#fff"] + }, + { + "name": "Sel", + "colors": ["#00467F", "#A5CC82"] + }, + { + "name": "Afternoon", + "colors": ["#000C40", "#607D8B"] + }, + { + "name": "Skyline", + "colors": ["#1488CC", "#2B32B2"] + }, + { + "name": "DIMIGO", + "colors": ["#ec008c", "#fc6767"] + }, + { + "name": "Purple Love", + "colors": ["#cc2b5e", "#753a88"] + }, + { + "name": "Sexy Blue", + "colors": ["#2193b0", "#6dd5ed"] + }, + { + "name": "Blooker20", + "colors": ["#e65c00", "#F9D423"] + }, + { + "name": "Sea Blue", + "colors": ["#2b5876", "#4e4376"] + }, + { + "name": "Nimvelo", + "colors": ["#314755", "#26a0da"] + }, + { + "name": "Hazel", + "colors": ["#77A1D3", "#79CBCA", "#E684AE"] + }, + { + "name": "Noon to Dusk", + "colors": ["#ff6e7f", "#bfe9ff"] + }, + { + "name": "YouTube", + "colors": ["#e52d27", "#b31217"] + }, + { + "name": "Cool Brown", + "colors": ["#603813", "#b29f94"] + }, + { + "name": "Harmonic Energy", + "colors": ["#16A085", "#F4D03F"] + }, + { + "name": "Playing with Reds", + "colors": ["#D31027", "#EA384D"] + }, + { + "name": "Sunny Days", + "colors": ["#EDE574", "#E1F5C4"] + }, + { + "name": "Green Beach", + "colors": ["#02AAB0", "#00CDAC"] + }, + { + "name": "Intuitive Purple", + "colors": ["#DA22FF", "#9733EE"] + }, + { + "name": "Emerald Water", + "colors": ["#348F50", "#56B4D3"] + }, + { + "name": "Lemon Twist", + "colors": ["#3CA55C", "#B5AC49"] + }, + { + "name": "Monte Carlo", + "colors": ["#CC95C0", "#DBD4B4", "#7AA1D2"] + }, + { + "name": "Horizon", + "colors": ["#003973", "#E5E5BE"] + }, + { + "name": "Rose Water", + "colors": ["#E55D87", "#5FC3E4"] + }, + { + "name": "Frozen", + "colors": ["#403B4A", "#E7E9BB"] + }, + { + "name": "Mango Pulp", + "colors": ["#F09819", "#EDDE5D"] + }, + { + "name": "Bloody Mary", + "colors": ["#FF512F", "#DD2476"] + }, + { + "name": "Aubergine", + "colors": ["#AA076B", "#61045F"] + }, + { + "name": "Aqua Marine", + "colors": ["#1A2980", "#26D0CE"] + }, + { + "name": "Sunrise", + "colors": ["#FF512F", "#F09819"] + }, + { + "name": "Purple Paradise", + "colors": ["#1D2B64", "#F8CDDA"] + }, + { + "name": "Stripe", + "colors": ["#1FA2FF", "#12D8FA", "#A6FFCB"] + }, + { + "name": "Sea Weed", + "colors": ["#4CB8C4", "#3CD3AD"] + }, + { + "name": "Pinky", + "colors": ["#DD5E89", "#F7BB97"] + }, + { + "name": "Cherry", + "colors": ["#EB3349", "#F45C43"] + }, + { + "name": "Mojito", + "colors": ["#1D976C", "#93F9B9"] + }, + { + "name": "Juicy Orange", + "colors": ["#FF8008", "#FFC837"] + }, + { + "name": "Mirage", + "colors": ["#16222A", "#3A6073"] + }, + { + "name": "Steel Gray", + "colors": ["#1F1C2C", "#928DAB"] + }, + { + "name": "Kashmir", + "colors": ["#614385", "#516395"] + }, + { + "name": "Electric Violet", + "colors": ["#4776E6", "#8E54E9"] + }, + { + "name": "Venice Blue", + "colors": ["#085078", "#85D8CE"] + }, + { + "name": "Bora Bora", + "colors": ["#2BC0E4", "#EAECC6"] + }, + { + "name": "Moss", + "colors": ["#134E5E", "#71B280"] + }, + { + "name": "Shroom Haze", + "colors": ["#5C258D", "#4389A2"] + }, + { + "name": "Mystic", + "colors": ["#757F9A", "#D7DDE8"] + }, + { + "name": "Midnight City", + "colors": ["#232526", "#414345"] + }, + { + "name": "Sea Blizz", + "colors": ["#1CD8D2", "#93EDC7"] + }, + { + "name": "Opa", + "colors": ["#3D7EAA", "#FFE47A"] + }, + { + "name": "Titanium", + "colors": ["#283048", "#859398"] + }, + { + "name": "Mantle", + "colors": ["#24C6DC", "#514A9D"] + }, + { + "name": "Dracula", + "colors": ["#DC2424", "#4A569D"] + }, + { + "name": "Peach", + "colors": ["#ED4264", "#FFEDBC"] + }, + { + "name": "Moonrise", + "colors": ["#DAE2F8", "#D6A4A4"] + }, + { + "name": "Clouds", + "colors": ["#ECE9E6", "#FFFFFF"] + }, + { + "name": "Stellar", + "colors": ["#7474BF", "#348AC7"] + }, + { + "name": "Bourbon", + "colors": ["#EC6F66", "#F3A183"] + }, + { + "name": "Calm Darya", + "colors": ["#5f2c82", "#49a09d"] + }, + { + "name": "Influenza", + "colors": ["#C04848", "#480048"] + }, + { + "name": "Shrimpy", + "colors": ["#e43a15", "#e65245"] + }, + { + "name": "Army", + "colors": ["#414d0b", "#727a17"] + }, + { + "name": "Miaka", + "colors": ["#FC354C", "#0ABFBC"] + }, + { + "name": "Pinot Noir", + "colors": ["#4b6cb7", "#182848"] + }, + { + "name": "Day Tripper", + "colors": ["#f857a6", "#ff5858"] + }, + { + "name": "Namn", + "colors": ["#a73737", "#7a2828"] + }, + { + "name": "Blurry Beach", + "colors": ["#d53369", "#cbad6d"] + }, + { + "name": "Vasily", + "colors": ["#e9d362", "#333333"] + }, + { + "name": "A Lost Memory", + "colors": ["#DE6262", "#FFB88C"] + }, + { + "name": "Petrichor", + "colors": ["#666600", "#999966"] + }, + { + "name": "Jonquil", + "colors": ["#FFEEEE", "#DDEFBB"] + }, + { + "name": "Sirius Tamed", + "colors": ["#EFEFBB", "#D4D3DD"] + }, + { + "name": "Kyoto", + "colors": ["#c21500", "#ffc500"] + }, + { + "name": "Misty Meadow", + "colors": ["#215f00", "#e4e4d9"] + }, + { + "name": "Aqualicious", + "colors": ["#50C9C3", "#96DEDA"] + }, + { + "name": "Moor", + "colors": ["#616161", "#9bc5c3"] + }, + { + "name": "Almost", + "colors": ["#ddd6f3", "#faaca8"] + }, + { + "name": "Forever Lost", + "colors": ["#5D4157", "#A8CABA"] + }, + { + "name": "Winter", + "colors": ["#E6DADA", "#274046"] + }, + { + "name": "Nelson", + "colors": ["#f2709c", "#ff9472"] + }, + { + "name": "Autumn", + "colors": ["#DAD299", "#B0DAB9"] + }, + { + "name": "Candy", + "colors": ["#D3959B", "#BFE6BA"] + }, + { + "name": "Reef", + "colors": ["#00d2ff", "#3a7bd5"] + }, + { + "name": "The Strain", + "colors": ["#870000", "#190A05"] + }, + { + "name": "Dirty Fog", + "colors": ["#B993D6", "#8CA6DB"] + }, + { + "name": "Earthly", + "colors": ["#649173", "#DBD5A4"] + }, + { + "name": "Virgin", + "colors": ["#C9FFBF", "#FFAFBD"] + }, + { + "name": "Ash", + "colors": ["#606c88", "#3f4c6b"] + }, + { + "name": "Cherryblossoms", + "colors": ["#FBD3E9", "#BB377D"] + }, + { + "name": "Parklife", + "colors": ["#ADD100", "#7B920A"] + }, + { + "name": "Dance To Forget", + "colors": ["#FF4E50", "#F9D423"] + }, + { + "name": "Starfall", + "colors": ["#F0C27B", "#4B1248"] + }, + { + "name": "Red Mist", + "colors": ["#000000", "#e74c3c"] + }, + { + "name": "Teal Love", + "colors": ["#AAFFA9", "#11FFBD"] + }, + { + "name": "Neon Life", + "colors": ["#B3FFAB", "#12FFF7"] + }, + { + "name": "Man of Steel", + "colors": ["#780206", "#061161"] + }, + { + "name": "Amethyst", + "colors": ["#9D50BB", "#6E48AA"] + }, + { + "name": "Cheer Up Emo Kid", + "colors": ["#556270", "#FF6B6B"] + }, + { + "name": "Shore", + "colors": ["#70e1f5", "#ffd194"] + }, + { + "name": "Facebook Messenger", + "colors": ["#00c6ff", "#0072ff"] + }, + { + "name": "SoundCloud", + "colors": ["#fe8c00", "#f83600"] + }, + { + "name": "Behongo", + "colors": ["#52c234", "#061700"] + }, + { + "name": "ServQuick", + "colors": ["#485563", "#29323c"] + }, + { + "name": "Friday", + "colors": ["#83a4d4", "#b6fbff"] + }, + { + "name": "Martini", + "colors": ["#FDFC47", "#24FE41"] + }, + { + "name": "Metallic Toad", + "colors": ["#abbaab", "#ffffff"] + }, + { + "name": "Between The Clouds", + "colors": ["#73C8A9", "#373B44"] + }, + { + "name": "Crazy Orange I", + "colors": ["#D38312", "#A83279"] + }, + { + "name": "Hersheys", + "colors": ["#1e130c", "#9a8478"] + }, + { + "name": "Talking To Mice Elf", + "colors": ["#948E99", "#2E1437"] + }, + { + "name": "Purple Bliss", + "colors": ["#360033", "#0b8793"] + }, + { + "name": "Predawn", + "colors": ["#FFA17F", "#00223E"] + }, + { + "name": "Endless River", + "colors": ["#43cea2", "#185a9d"] + }, + { + "name": "Pastel Orange at the Sun", + "colors": ["#ffb347", "#ffcc33"] + }, + { + "name": "Twitch", + "colors": ["#6441A5", "#2a0845"] + }, + { + "name": "Atlas", + "colors": ["#FEAC5E", "#C779D0", "#4BC0C8"] + }, + { + "name": "Instagram", + "colors": ["#833ab4", "#fd1d1d", "#fcb045"] + }, + { + "name": "Flickr", + "colors": ["#ff0084", "#33001b"] + }, + { + "name": "Vine", + "colors": ["#00bf8f", "#001510"] + }, + { + "name": "Turquoise flow", + "colors": ["#136a8a", "#267871"] + }, + { + "name": "Portrait", + "colors": ["#8e9eab", "#eef2f3"] + }, + { + "name": "Virgin America", + "colors": ["#7b4397", "#dc2430"] + }, + { + "name": "Koko Caramel", + "colors": ["#D1913C", "#FFD194"] + }, + { + "name": "Fresh Turboscent", + "colors": ["#F1F2B5", "#135058"] + }, + { + "name": "Green to dark", + "colors": ["#6A9113", "#141517"] + }, + { + "name": "Ukraine", + "colors": ["#004FF9", "#FFF94C"] + }, + { + "name": "Curiosity blue", + "colors": ["#525252", "#3d72b4"] + }, + { + "name": "Dark Knight", + "colors": ["#BA8B02", "#181818"] + }, + { + "name": "Piglet", + "colors": ["#ee9ca7", "#ffdde1"] + }, + { + "name": "Lizard", + "colors": ["#304352", "#d7d2cc"] + }, + { + "name": "Sage Persuasion", + "colors": ["#CCCCB2", "#757519"] + }, + { + "name": "Between Night and Day", + "colors": ["#2c3e50", "#3498db"] + }, + { + "name": "Timber", + "colors": ["#fc00ff", "#00dbde"] + }, + { + "name": "Passion", + "colors": ["#e53935", "#e35d5b"] + }, + { + "name": "Clear Sky", + "colors": ["#005C97", "#363795"] + }, + { + "name": "Master Card", + "colors": ["#f46b45", "#eea849"] + }, + { + "name": "Back To Earth", + "colors": ["#00C9FF", "#92FE9D"] + }, + { + "name": "Deep Purple", + "colors": ["#673AB7", "#512DA8"] + }, + { + "name": "Little Leaf", + "colors": ["#76b852", "#8DC26F"] + }, + { + "name": "Netflix", + "colors": ["#8E0E00", "#1F1C18"] + }, + { + "name": "Light Orange", + "colors": ["#FFB75E", "#ED8F03"] + }, + { + "name": "Green and Blue", + "colors": ["#c2e59c", "#64b3f4"] + }, + { + "name": "Poncho", + "colors": ["#403A3E", "#BE5869"] + }, + { + "name": "Back to the Future", + "colors": ["#C02425", "#F0CB35"] + }, + { + "name": "Blush", + "colors": ["#B24592", "#F15F79"] + }, + { + "name": "Inbox", + "colors": ["#457fca", "#5691c8"] + }, + { + "name": "Purplin", + "colors": ["#6a3093", "#a044ff"] + }, + { + "name": "Pale Wood", + "colors": ["#eacda3", "#d6ae7b"] + }, + { + "name": "Haikus", + "colors": ["#fd746c", "#ff9068"] + }, + { + "name": "Pizelex", + "colors": ["#114357", "#F29492"] + }, + { + "name": "Joomla", + "colors": ["#1e3c72", "#2a5298"] + }, + { + "name": "Christmas", + "colors": ["#2F7336", "#AA3A38"] + }, + { + "name": "Minnesota Vikings", + "colors": ["#5614B0", "#DBD65C"] + }, + { + "name": "Miami Dolphins", + "colors": ["#4DA0B0", "#D39D38"] + }, + { + "name": "Forest", + "colors": ["#5A3F37", "#2C7744"] + }, + { + "name": "Nighthawk", + "colors": ["#2980b9", "#2c3e50"] + }, + { + "name": "Superman", + "colors": ["#0099F7", "#F11712"] + }, + { + "name": "Suzy", + "colors": ["#834d9b", "#d04ed6"] + }, + { + "name": "Dark Skies", + "colors": ["#4B79A1", "#283E51"] + }, + { + "name": "Deep Space", + "colors": ["#000000", "#434343"] + }, + { + "name": "Decent", + "colors": ["#4CA1AF", "#C4E0E5"] + }, + { + "name": "Colors Of Sky", + "colors": ["#E0EAFC", "#CFDEF3"] + }, + { + "name": "Purple White", + "colors": ["#BA5370", "#F4E2D8"] + }, + { + "name": "Ali", + "colors": ["#ff4b1f", "#1fddff"] + }, + { + "name": "Alihossein", + "colors": ["#f7ff00", "#db36a4"] + }, + { + "name": "Shahabi", + "colors": ["#a80077", "#66ff00"] + }, + { + "name": "Red Ocean", + "colors": ["#1D4350", "#A43931"] + }, + { + "name": "Tranquil", + "colors": ["#EECDA3", "#EF629F"] + }, + { + "name": "Transfile", + "colors": ["#16BFFD", "#CB3066"] + }, + + { + "name": "Sylvia", + "colors": ["#ff4b1f", "#ff9068"] + }, + { + "name": "Sweet Morning", + "colors": ["#FF5F6D", "#FFC371"] + }, + { + "name": "Politics", + "colors": ["#2196f3", "#f44336"] + }, + { + "name": "Bright Vault", + "colors": ["#00d2ff", "#928DAB"] + }, + { + "name": "Solid Vault", + "colors": ["#3a7bd5", "#3a6073"] + }, + { + "name": "Sunset", + "colors": ["#0B486B", "#F56217"] + }, + { + "name": "Grapefruit Sunset", + "colors": ["#e96443", "#904e95"] + }, + { + "name": "Deep Sea Space", + "colors": ["#2C3E50", "#4CA1AF"] + }, + { + "name": "Dusk", + "colors": ["#2C3E50", "#FD746C"] + }, + { + "name": "Minimal Red", + "colors": ["#F00000", "#DC281E"] + }, + { + "name": "Royal", + "colors": ["#141E30", "#243B55"] + }, + { + "name": "Mauve", + "colors": ["#42275a", "#734b6d"] + }, + { + "name": "Frost", + "colors": ["#000428", "#004e92"] + }, + { + "name": "Lush", + "colors": ["#56ab2f", "#a8e063"] + }, + { + "name": "Firewatch", + "colors": ["#cb2d3e", "#ef473a"] + }, + { + "name": "Sherbert", + "colors": ["#f79d00", "#64f38c"] + }, + { + "name": "Blood Red", + "colors": ["#f85032", "#e73827"] + }, + { + "name": "Sun on the Horizon", + "colors": ["#fceabb", "#f8b500"] + }, + { + "name": "IIIT Delhi", + "colors": ["#808080", "#3fada8"] + }, + { + "name": "Jupiter", + "colors": ["#ffd89b", "#19547b"] + }, + { + "name": "50 Shades of Grey", + "colors": ["#bdc3c7", "#2c3e50"] + }, + { + "name": "Dania", + "colors": ["#BE93C5", "#7BC6CC"] + }, + { + "name": "Limeade", + "colors": ["#A1FFCE", "#FAFFD1"] + }, + { + "name": "Disco", + "colors": ["#4ECDC4", "#556270"] + }, + { + "name": "Love Couple", + "colors": ["#3a6186", "#89253e"] + }, + { + "name": "Azure Pop", + "colors": ["#ef32d9", "#89fffd"] + }, + { + "name": "Nepal", + "colors": ["#de6161", "#2657eb"] + }, + { + "name": "Cosmic Fusion", + "colors": ["#ff00cc", "#333399"] + }, + { + "name": "Snapchat", + "colors": ["#fffc00", "#ffffff"] + }, + { + "name": "Ed's Sunset Gradient", + "colors": ["#ff7e5f", "#feb47b"] + }, + { + "name": "Brady Brady Fun Fun", + "colors": ["#00c3ff", "#ffff1c"] + }, + { + "name": "Black Rosé", + "colors": ["#f4c4f3", "#fc67fa"] + }, + { + "name": "80's Purple", + "colors": ["#41295a", "#2F0743"] + }, + { + "name": "Radar", + "colors": ["#A770EF", "#CF8BF3", "#FDB99B"] + }, + { + "name": "Ibiza Sunset", + "colors": ["#ee0979", "#ff6a00"] + }, + { + "name": "Dawn", + "colors": ["#F3904F", "#3B4371"] + }, + { + "name": "Mild", + "colors": ["#67B26F", "#4ca2cd"] + }, + { + + "name": "Vice City", + "colors": ["#3494E6", "#EC6EAD"] + }, + { + "name": "Jaipur", + "colors": ["#DBE6F6", "#C5796D"] + + }, + { + "name": "Jodhpur", + "colors": ["#9CECFB", "#65C7F7", "#0052D4"] + + }, + { + "name": "Cocoaa Ice", + "colors": ["#c0c0aa", "#1cefff"] + }, + { + "name": "EasyMed", + "colors": ["#DCE35B", "#45B649"] + }, + { + "name": "Rose Colored Lenses", + "colors": ["#E8CBC0", "#636FA4"] + }, + { + "name": "What lies Beyond", + "colors": ["#F0F2F0", "#000C40"] + }, + { + "name": "Roseanna", + "colors": ["#FFAFBD", "#ffc3a0"] + }, + { + "name": "Honey Dew", + "colors": ["#43C6AC", "#F8FFAE"] + }, + { + "name": "Under the Lake", + "colors": ["#093028", "#237A57"] + }, + { + "name": "The Blue Lagoon", + "colors": ["#43C6AC", "#191654"] + }, + { + "name": "Can You Feel The Love Tonight", + "colors": ["#4568DC", "#B06AB3"] + }, + { + "name": "Very Blue", + "colors": ["#0575E6", "#021B79"] + }, + { + "name": "Love and Liberty", + "colors": ["#200122", "#6f0000"] + }, + { + "name": "Orca", + "colors": ["#44A08D", "#093637"] + }, + { + "name": "Venice", + "colors": ["#6190E8", "#A7BFE8"] + }, + { + "name": "Pacific Dream", + "colors": ["#34e89e", "#0f3443"] + }, + { + "name": "Learning and Leading", + "colors": ["#F7971E", "#FFD200"] + }, + { + "name": "Celestial", + "colors": ["#C33764", "#1D2671"] + }, + { + "name": "Purplepine", + "colors": ["#20002c", "#cbb4d4"] + }, + { + "name": "Sha la la", + "colors": ["#D66D75", "#E29587"] + }, + { + "name": "Mini", + "colors": ["#30E8BF", "#FF8235"] + }, + { + "name": "Maldives", + "colors": ["#B2FEFA", "#0ED2F7"] + }, + { + "name": "Cinnamint", + "colors": ["#4AC29A", "#BDFFF3"] + }, + { + "name": "Html", + "colors": ["#E44D26", "#F16529"] + }, + { + "name": "Coal", + "colors": ["#EB5757", "#000000"] + }, + { + "name": "Sunkist", + "colors": ["#F2994A", "#F2C94C"] + }, + { + "name": "Blue Skies", + "colors": ["#56CCF2", "#2F80ED"] + }, + { + "name": "Chitty Chitty Bang Bang", + "colors": ["#007991", "#78ffd6"] + }, + { + "name": "Visions of Grandeur", + "colors": ["#000046", "#1CB5E0"] + }, + { + "name": "Crystal Clear", + "colors": ["#159957", "#155799"] + }, + { + "name": "Mello", + "colors": ["#c0392b", "#8e44ad"] + }, + { + "name": "Compare Now", + "colors": ["#EF3B36", "#FFFFFF"] + }, + { + "name": "Meridian", + "colors": ["#283c86", "#45a247"] + }, + { + "name": "Relay", + "colors": ["#3A1C71", "#D76D77", "#FFAF7B"] + }, + { + "name": "Alive", + "colors": ["#CB356B", "#BD3F32"] + }, + { + "name": "Scooter", + "colors": ["#36D1DC", "#5B86E5"] + }, + { + "name": "Terminal", + "colors": ["#000000", "#0f9b0f"] + }, + { + "name": "Telegram", + "colors": ["#1c92d2", "#f2fcfe"] + }, + { + "name": "Crimson Tide", + "colors": ["#642B73", "#C6426E"] + }, + { + "name": "Socialive", + "colors": ["#06beb6", "#48b1bf"] + }, + { + "name": "Subu", + "colors": ["#0cebeb", "#20e3b2", "#29ffc6"] + }, + { + "name": "Broken Hearts", + "colors": ["#d9a7c7", "#fffcdc"] + }, + { + "name": "Kimoby Is The New Blue", + "colors": ["#396afc", "#2948ff"] + }, + { + "name": "Dull", + "colors": ["#C9D6FF", "#E2E2E2"] + }, + { + "name": "Purpink", + "colors": ["#7F00FF", "#E100FF"] + }, + { + "name": "Orange Coral", + "colors": ["#ff9966", "#ff5e62"] + }, + { + "name": "Summer", + "colors": ["#22c1c3", "#fdbb2d"] + }, + { + "name": "King Yna", + "colors": ["#1a2a6c", "#b21f1f", "#fdbb2d"] + }, + { + "name": "Velvet Sun", + "colors": ["#e1eec3", "#f05053"] + }, + { + "name": "Zinc", + "colors": ["#ADA996", "#F2F2F2", "#DBDBDB", "#EAEAEA"] + }, + { + "name": "Hydrogen", + "colors": ["#667db6", "#0082c8", "#0082c8", "#667db6"] + }, + { + "name": "Argon", + "colors": ["#03001e", "#7303c0", "#ec38bc", "#fdeff9"] + }, + { + "name": "Lithium", + "colors": ["#6D6027", "#D3CBB8"] + }, + { + "name": "Digital Water", + "colors": ["#74ebd5","#ACB6E5"] + }, + { + "name": "Orange Fun", + "colors": ["#fc4a1a", "#f7b733"] + }, + { + "name": "Rainbow Blue", + "colors": ["#00F260", "#0575E6"] + }, + { + "name": "Pink Flavour", + "colors": ["#800080", "#ffc0cb"] + }, + { + "name": "Sulphur", + "colors": ["#CAC531", "#F3F9A7"] + }, + { + "name": "Selenium", + "colors": ["#3C3B3F", "#605C3C"] + }, + { + "name": "Delicate", + "colors": ["#D3CCE3", "#E9E4F0"] + }, + { + + "name": "Ohhappiness", + "colors": ["#00b09b", "#96c93d"] + }, + { + "name": "Lawrencium", + "colors": ["#0f0c29", "#302b63", "#24243e"] + }, + { + "name": "Relaxing red", + "colors": ["#fffbd5", "#b20a2c"] + }, + { + "name": "Taran Tado", + "colors": ["#23074d", "#cc5333"] + }, + { + "name": "Bighead", + "colors": ["#c94b4b", "#4b134f"] + }, + { + "name": "Sublime Vivid", + "colors": ["#FC466B", "#3F5EFB"] + }, + { + "name": "Sublime Light", + "colors": ["#FC5C7D", "#6A82FB"] + }, + { + "name": "Pun Yeta", + "colors": ["#108dc7", "#ef8e38"] + }, + { + "name": "Quepal", + "colors": ["#11998e", "#38ef7d"] + }, + { + "name": "Sand to Blue", + "colors": ["#3E5151", "#DECBA4"] + }, + { + "name": "Wedding Day Blues", + "colors": ["#40E0D0", "#FF8C00", "#FF0080"] + }, + { + "name": "Shifter", + "colors": ["#bc4e9c", "#f80759"] + }, + { + "name": "Red Sunset", + "colors": ["#355C7D", "#6C5B7B", "#C06C84"] + }, + { + "name": "Moon Purple", + "colors": ["#4e54c8", "#8f94fb"] + }, + { + "name": "Pure Lust", + "colors": ["#333333", "#dd1818"] + }, + { + "name": "Slight Ocean View", + "colors": ["#a8c0ff", "#3f2b96"] + }, + { + "name": "eXpresso", + "colors": ["#ad5389", "#3c1053"] + }, + { + "name": "Shifty", + "colors": ["#636363", "#a2ab58"] + }, + { + "name": "Vanusa", + "colors": ["#DA4453", "#89216B"] + }, + { + "name": "Evening Night", + "colors": ["#005AA7", "#FFFDE4"] + }, + { + "name": "Magic", + "colors": ["#59C173", "#a17fe0", "#5D26C1"] + }, + { + "name": "Margo", + "colors": ["#FFEFBA", "#FFFFFF"] + }, + { + "name": "Blue Raspberry", + "colors": ["#00B4DB", "#0083B0"] + }, + { + "name": "Citrus Peel", + "colors": ["#FDC830", "#F37335"] + }, + { + "name": "Sin City Red", + "colors": ["#ED213A", "#93291E"] + }, + { + "name": "Rastafari", + "colors": ["#1E9600", "#FFF200", "#FF0000"] + }, + { + "name": "Summer Dog", + "colors": ["#a8ff78", "#78ffd6"] + }, + { + "name": "Wiretap", + "colors": ["#8A2387", "#E94057", "#F27121"] + }, + { + "name": "Burning Orange", + "colors": ["#FF416C", "#FF4B2B"] + }, + { + "name": "Ultra Voilet", + "colors": ["#654ea3", "#eaafc8"] + }, + { + "name": "By Design", + "colors": ["#009FFF", "#ec2F4B"] + }, + { + "name": "Kyoo Tah", + "colors": ["#544a7d", "#ffd452"] + }, + { + "name": "Kye Meh", + "colors": ["#8360c3", "#2ebf91"] + }, + { + "name": "Kyoo Pal", + "colors": ["#dd3e54", "#6be585"] + }, + { + "name": "Metapolis", + "colors": ["#659999", "#f4791f"] + }, + { + "name": "Flare", + "colors": ["#f12711", "#f5af19"] + }, + { + "name": "Witching Hour", + "colors": ["#c31432", "#240b36"] + }, + { + "name": "Azur Lane", + "colors": ["#7F7FD5", "#86A8E7", "#91EAE4"] + }, + { + "name": "Neuromancer", + "colors": ["#f953c6", "#b91d73"] + }, + { + "name": "Harvey", + "colors": ["#1f4037", "#99f2c8"] + }, + { + "name": "Amin", + "colors": ["#8E2DE2", "#4A00E0"] + }, + { + "name": "Memariani", + "colors": ["#aa4b6b", "#6b6b83" , "#3b8d99"] + }, + { + "name": "Yoda", + "colors": ["#FF0099", "#493240"] + }, + { + "name": "Cool Sky", + "colors": ["#2980B9", "#6DD5FA", "#FFFFFF"] + }, + { + "name": "Dark Ocean", + "colors": ["#373B44", "#4286f4"] + }, + { + "name": "Evening Sunshine", + "colors": ["#b92b27", "#1565C0"] + }, + { + "name": "JShine", + "colors": ["#12c2e9", "#c471ed", "#f64f59"] + }, + { + "name": "Moonlit Asteroid", + "colors": ["#0F2027", "#203A43", "#2C5364"] + }, + { + "name": "MegaTron", + "colors": ["#C6FFDD", "#FBD786", "#f7797d"] + }, + { + "name": "Cool Blues", + "colors": ["#2193b0", "#6dd5ed"] + }, + { + "name": "Piggy Pink", + "colors": ["#ee9ca7", "#ffdde1"] + }, + { + "name": "Grade Grey", + "colors": ["#bdc3c7","#2c3e50"] + }, + { + "name": "Telko", + "colors": ["#F36222", "#5CB644", "#007FC3"] + }, + { + "name": "Zenta", + "colors": ["#2A2D3E","#FECB6E"] + }, + { + "name": "Electric Peacock", + "colors": ["#8a2be2","#0000cd","#228b22","#ccff00"] + }, + { + "name": "Under Blue Green", + "colors": ["#051937","#004d7a","#008793","#00bf72","#a8eb12"] + }, + { + "name": "Lensod", + "colors": ["#6025F5","#FF5555"] + }, + { + "name": "Newspaper", + "colors": ["#8a2be2","#ffa500","#f8f8ff"] + }, + { + "name": "Dark Blue Gradient", + "colors": ["#2774ae", "#002E5D", "#002E5D"] + }, + { + "name": "Dark Blu Two", + "colors": ["#004680","#4484BA"] + }, + { + "name": "Lemon Lime", + "colors":["#7ec6bc","#ebe717"] + }, + { + "name": "Beleko", + "colors": ["#ff1e56", "#f9c942", "#1e90ff"] + }, + { + "name": "Mango Papaya", + "colors": ["#de8a41","#2ada53"] + }, + { + "name": "Unicorn Rainbow", + "colors": ["#f7f0ac","#acf7f0","#f0acf7"] + }, + { + "name": "Flame", + "colors": ["#ff0000", "#fdcf58"] + }, + { + "name": "Blue Red", + "colors": ["#36B1C7", "#960B33"] + }, + { + "name" : "Twitter", + "colors" : ["#1DA1F2", "#009ffc"] + }, + { + "name": "Blooze", + "colors": ["#6da6be", "#4b859e", "#6da6be"] + }, + { + "name": "Blue Slate", + "colors": ["#B5B9FF", "#2B2C49"] + }, + { + "name": "Space Light Green", + "colors": ["#9FA0A8", "#5C7852"] + }, + { + "name": "Flower", + "colors": ["#DCFFBD", "#CC86D1"] + }, + { + "name": "Elate The Euge", + "colors": ["#8BDEDA", "43ADD0", "998EE0", "E17DC2", "EF9393"] + }, + { + "name": "Peach Sea", + "colors": ["#E6AE8C", "#A8CECF"] + }, + { + "name": "Abbas", + "colors": ["#00fff0","#0083fe"] + }, + { + "name": "Winter Woods", + "colors": ["#333333", "#a2ab58", "#A43931"] + }, + { + "name": "Ameena", + "colors": ["#0c0c6d", "#de512b", "#98d0c1", "#5bb226", "#023c0d"] + }, + { + "name": "Emerald Sea", + "colors": ["#05386b","#5cdb95"] + }, + { + "name": "Bleem", + "colors": ["#4284DB", "#29EAC4"] + }, + { + "name": "Coffee Gold", + "colors": ["#554023", "#c99846"] + }, + { + "name": "Compass", + "colors": ["#516b8b", "#056b3b"] + }, + { + "name": "Andreuzza's", + "colors": ["#D70652", "#FF025E"] + }, + { + "name": "Moonwalker", + "colors": ["#152331", "#000000"] + }, + { + "name": "Whinehouse", + "colors": ["#f7f7f7", "#b9a0a0", "#794747", "#4e2020", "#111111"] + }, + { + "name": "Hyper Blue", + "colors": ["#59CDE9", "#0A2A88"] + }, + { + "name": "Racker", + "colors": ["#EB0000","#95008A","#3300FC"] + }, + { + "name": "After the Rain", + "colors": ["#ff75c3", "#ffa647", "#ffe83f", "#9fff5b", "#70e2ff", "#cd93ff"] + }, + { + "name": "Neon Green", + "colors": ["#81ff8a", "#64965e"] + }, + { + "name": "Dusty Grass", + "colors": ["#d4fc79", "#96e6a1"] + }, + { + "name": "Visual Blue", + "colors": ["#003d4d", "#00c996"] + } +] diff --git a/tool/bin/generate_gradient_samples.dart b/tool/bin/generate_gradient_samples.dart new file mode 100644 index 0000000..eeda8a2 --- /dev/null +++ b/tool/bin/generate_gradient_samples.dart @@ -0,0 +1,67 @@ +import 'dart:io'; + +import 'package:file/local.dart'; +import 'package:path/path.dart'; +import 'package:tool/tool.dart' as tool; + +void main(List arguments) { + try { + println('Generating gradient samples... 🚀', addNewLineToBeginning: true); + + final gradientsJsonPath = join(dirname(Platform.script.toFilePath()), '..', + 'assets', 'gradients.json'); + + final generatedGradientSamplesPath = join( + dirname(Platform.script.toFilePath()), + '../..', + 'lib', + 'generated', + 'gradient_samples.dart'); + + tool.generateGradientSamples( + gradientsJsonPath: gradientsJsonPath, + generatedGradientSamplesPath: generatedGradientSamplesPath, + fileSystem: const LocalFileSystem(), + onGenerationComplete: () { + // Analyze the generated file + + final dartAnalyzeResult = + Process.runSync('dart', ['analyze', generatedGradientSamplesPath]); + + if (dartAnalyzeResult.exitCode != 0) { + throw Exception( + 'Failed to analyze the generated gradient_samples.dart file'); + } + + // Format the generated file + + final dartFormatResult = + Process.runSync('dart', ['format', generatedGradientSamplesPath]); + + if (dartFormatResult.exitCode != 0) { + throw Exception( + 'Failed to format the generated gradient_samples.dart file'); + } + + println('Gradient samples generated! 🎉'); + }, + ); + } catch (e) { + print('Error generating gradient samples 😢:'); + println(' $e'); + } +} + +/// Prints the [object] to the console with a new line at the end. +/// +/// Optionally, a new line can be added to the beginning of the output by +/// setting [addNewLineToBeginning] to `true`. +/// +/// [addNewLineToBeginning] defaults to `false`. +void println(Object? object, {bool addNewLineToBeginning = false}) { + if (addNewLineToBeginning) { + print('\n'); + } + print(object); + print('\n'); +} diff --git a/tool/lib/tool.dart b/tool/lib/tool.dart new file mode 100644 index 0000000..365e4dd --- /dev/null +++ b/tool/lib/tool.dart @@ -0,0 +1,114 @@ +import 'dart:convert'; +import 'package:file/file.dart'; + +/// Generates the gradient samples used in the app from the gradients.json file. +/// +/// Throws an [Exception] if the gradients.json file is not found. +/// +/// [onGenerationComplete] is called when the generation is complete. +void generateGradientSamples({ + required String gradientsJsonPath, + required String generatedGradientSamplesPath, + required FileSystem fileSystem, + required void Function() onGenerationComplete, +}) { + try { + ////////////////////////////////////////////////////// + /////// Read and parse the gradients.json file /////// + ////////////////////////////////////////////////////// + + final gradientsJsonFile = fileSystem.file(gradientsJsonPath); + + if (!gradientsJsonFile.existsSync()) { + throw Exception('gradients.json file not found'); + } + + final gradientsJson = gradientsJsonFile.readAsStringSync(); + + final gradientsJsonDecoded = (json.decode( + gradientsJson, + ) as List) + .cast>(); + + /////////////////////////////////////////////////////// + /// Generate the gradient_samples.dart file content /// + /////////////////////////////////////////////////////// + + final buffer = StringBuffer(); + + buffer.writeln('// This is a generated file. Do not edit it manually.'); + buffer.writeln('//'); + buffer.writeln( + '// To regenerate this file, run the following command from the project root:'); + buffer.writeln('// dart tool/bin/generate_gradient_samples.dart\n'); + + buffer.writeln('import \'package:flutter/material.dart\';\n'); + + buffer.writeln('class _GradientSample {'); + buffer.writeln( + ' const _GradientSample({required this.name, required this.colors});\n'); + buffer.writeln(' final String name;'); + buffer.writeln(' final List colors;'); + buffer.writeln('}\n'); + + buffer.write('const gradientSamples = <_GradientSample>[\n'); + + for (final gradient in gradientsJsonDecoded) { + final gradientName = gradient['name'] as String; + final gradientColors = gradient['colors'] as List; + + final gradientColorsHex = gradientColors + .map((color) => color as String) + .map((color) { + String updatedColor = color; + + /// Remove the # prefix if present + if (color.startsWith('#')) { + updatedColor = color.substring(1); + } + + /// Convert 3 digit hex color to 6 digit hex color + /// + /// For example, #fff to #ffffff + if (updatedColor.length == 3) { + final r = updatedColor[0]; + final g = updatedColor[1]; + final b = updatedColor[2]; + + updatedColor = '$r$r$g$g$b$b'; + } + + return '0xFF$updatedColor'; + }) + .map((color) => 'Color($color)') + .toList(); + + buffer.write('_GradientSample('); + buffer.write('name: \'${gradientName.replaceAll("'", "\\'")}\', '); + buffer.write('colors: ['); + buffer.writeAll(gradientColorsHex, ', '); + buffer.write(',]'); + buffer.write('),\n'); + } + + buffer.write('];'); + + ////////////////////////////////////////////////////// + /////// Write the generated gradient_samples.dart ///// + ////////////////////////////////////////////////////// + + final generatedGradientSamplesFile = + fileSystem.file(generatedGradientSamplesPath); + + generatedGradientSamplesFile.writeAsStringSync(buffer.toString()); + + ////////////////////////////////////////////////////// + /////// Call onGenerationComplete callback //////////// + //////////////////////////////////////////////////////// + + onGenerationComplete(); + } catch (e) { + // Rethrowing so that the caller handles the exception. + rethrow; + } +} diff --git a/tool/pubspec.lock b/tool/pubspec.lock new file mode 100644 index 0000000..1e196cf --- /dev/null +++ b/tool/pubspec.lock @@ -0,0 +1,381 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + url: "https://pub.dev" + source: hosted + version: "67.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + url: "https://pub.dev" + source: hosted + version: "6.4.1" + args: + dependency: transitive + description: + name: args + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + url: "https://pub.dev" + source: hosted + version: "2.4.2" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + coverage: + dependency: transitive + description: + name: coverage + sha256: "8acabb8306b57a409bf4c83522065672ee13179297a6bb0cb9ead73948df7c76" + url: "https://pub.dev" + source: hosted + version: "1.7.2" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" + file: + dependency: "direct main" + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + js: + dependency: transitive + description: + name: js + sha256: "4186c61b32f99e60f011f7160e32c89a758ae9b1d0c6d28e2c02ef0382300e2b" + url: "https://pub.dev" + source: hosted + version: "0.7.0" + lints: + dependency: "direct dev" + description: + name: lints + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + logging: + dependency: transitive + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" + meta: + dependency: transitive + description: + name: meta + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + mime: + dependency: transitive + description: + name: mime + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" + url: "https://pub.dev" + source: hosted + version: "1.0.5" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + path: + dependency: "direct main" + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e + url: "https://pub.dev" + source: hosted + version: "1.1.2" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" + url: "https://pub.dev" + source: hosted + version: "0.10.12" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test: + dependency: "direct dev" + description: + name: test + sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073" + url: "https://pub.dev" + source: hosted + version: "1.25.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + url: "https://pub.dev" + source: hosted + version: "0.7.0" + test_core: + dependency: transitive + description: + name: test_core + sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4" + url: "https://pub.dev" + source: hosted + version: "0.6.0" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: a2662fb1f114f4296cf3f5a50786a2d888268d7776cf681aa17d660ffa23b246 + url: "https://pub.dev" + source: hosted + version: "14.0.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05" + url: "https://pub.dev" + source: hosted + version: "0.4.2" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: "939ab60734a4f8fa95feacb55804fa278de28bdeef38e616dc08e44a84adea23" + url: "https://pub.dev" + source: hosted + version: "2.4.3" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" +sdks: + dart: ">=3.2.6 <4.0.0" diff --git a/tool/pubspec.yaml b/tool/pubspec.yaml new file mode 100644 index 0000000..f071c06 --- /dev/null +++ b/tool/pubspec.yaml @@ -0,0 +1,17 @@ +name: tool +description: A sample command-line application. +version: 1.0.0 +# repository: https://github.com/my_org/my_repo + +environment: + sdk: ^3.2.6 + +# Add regular dependencies here. +dependencies: + file: ^7.0.0 + path: ^1.9.0 + # path: ^1.8.0 + +dev_dependencies: + lints: ^2.1.0 + test: ^1.24.0 \ No newline at end of file diff --git a/tool/test/tool_test.dart b/tool/test/tool_test.dart new file mode 100644 index 0000000..142eeed --- /dev/null +++ b/tool/test/tool_test.dart @@ -0,0 +1,93 @@ +import 'package:file/memory.dart'; +import 'package:path/path.dart'; +import 'package:tool/tool.dart'; +import 'package:test/test.dart'; + +void main() { + group('generateGradientSamples', () { + final gradientsJsonPath = 'assets/gradients.json'; + final generatedGradientSamplesPath = 'lib/generated/gradient_samples.dart'; + + late MemoryFileSystem memoryFileSystem; + + setUp(() { + memoryFileSystem = MemoryFileSystem(); + + memoryFileSystem.directory(dirname(gradientsJsonPath)).createSync(); + + memoryFileSystem + .directory(dirname(generatedGradientSamplesPath)) + .createSync(recursive: true); + }); + + test( + 'should generate correct gradient samples content at the specified path', + () { + memoryFileSystem.file(gradientsJsonPath).writeAsStringSync(''' + [ + { + "name": "Sample Gradient 1", + "colors": ["#FF0000", "#00FF00", "#0000FF"] + }, + { + "name": "Sample Gradient 2", + "colors": ["#FF0000", "#00FF00"] + } + ] + '''); + + generateGradientSamples( + gradientsJsonPath: gradientsJsonPath, + generatedGradientSamplesPath: generatedGradientSamplesPath, + fileSystem: memoryFileSystem, + onGenerationComplete: () {}, + ); + + final generatedGradientSamplesFile = + memoryFileSystem.file(generatedGradientSamplesPath); + + expect(generatedGradientSamplesFile.existsSync(), true); + + final generatedGradientSamplesFileContent = + generatedGradientSamplesFile.readAsStringSync(); + + final expectedGeneratedGradientSamplesFileContent = ''' +// This is a generated file. Do not edit it manually. +// +// To regenerate this file, run the following command from the project root: +// dart tool/bin/generate_gradient_samples.dart + +import 'package:flutter/material.dart'; + +class _GradientSample { + const _GradientSample({required this.name, required this.colors}); + + final String name; + final List colors; +} + +const gradientSamples = <_GradientSample>[ +_GradientSample(name: 'Sample Gradient 1', colors: [Color(0xFFFF0000), Color(0xFF00FF00), Color(0xFF0000FF),]), +_GradientSample(name: 'Sample Gradient 2', colors: [Color(0xFFFF0000), Color(0xFF00FF00),]), +]; +'''; + + expect(generatedGradientSamplesFileContent.trim(), + expectedGeneratedGradientSamplesFileContent.trim()); + }, + ); + + test('should throw an exception if the gradients.json file is not found', + () { + expect( + () => generateGradientSamples( + gradientsJsonPath: gradientsJsonPath, + generatedGradientSamplesPath: generatedGradientSamplesPath, + fileSystem: memoryFileSystem, + onGenerationComplete: () {}, + ), + throwsException, + ); + }); + }); +} diff --git a/web/index.html b/web/index.html index f01cdab..b89e758 100644 --- a/web/index.html +++ b/web/index.html @@ -1,122 +1,118 @@ - - - + + + + + + - - - - - + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - + Flutter Gradient Generator - Create Flutter Gradients + + + - Flutter Gradient Generator - Create Flutter Gradients - - - - - -
-
-

Flutter™ Gradient Generator

-
+ + + + -
-
-
-
-
-
+ +
+
+

Flutter™ Gradient Generator

+
-
-

- Flutter and the related logo are trademarks of Google LLC. We are - not endorsed by or affiliated with Google LLC. -

-

- Built - by - Victor Eronmosele -

-
+
+
+
+
+
-
- - - - - + +
+

+ Flutter and the related logo are trademarks of Google LLC. We are + not endorsed by or affiliated with Google LLC. +

+

+ Built + by + Victor Eronmosele +

+
+
+
+ + + + \ No newline at end of file diff --git a/web/manifest.json b/web/manifest.json index 4b599fb..b76ce21 100644 --- a/web/manifest.json +++ b/web/manifest.json @@ -8,7 +8,7 @@ "description": "Flutter Gradient Generator", "orientation": "portrait-primary", "prefer_related_applications": false, - "icons": [ + "icons": [ { "src": "/android-chrome-192x192.png", "sizes": "192x192", diff --git a/web/screenshot.png b/web/screenshot.png index f20621e..05292d7 100644 Binary files a/web/screenshot.png and b/web/screenshot.png differ