[LLM release] Dart 3.9 / Flutter 3.35 (#2701)

I got carried away with Gemini and basically rewrote CI and the release
process for the new LLM reality.

- Bump all SDK versions to the current beta (3.9.0-0)
- Run `flutter channel beta`
- Wrote `ci_script.dart` to replace the bash scripts
- Converted repository to pub workspace
- Added llm.md and release.md
- Added redirect for deprecated Samples Index

## Pre-launch Checklist

- [x] I read the [Flutter Style Guide] _recently_, and have followed its
advice.
- [x] I signed the [CLA].
- [x] I read the [Contributors Guide].
- [x] I have added sample code updates to the [changelog].
- [x] I updated/added relevant documentation (doc comments with `///`).
pull/2714/head
Eric Windmill 1 month ago committed by GitHub
parent 74378fc6f0
commit 9801f24ac8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,10 @@
{
"mcpServers": {
"dart": {
"command": "dart",
"args": [
"mcp-server"
]
}
}
}

@ -15,10 +15,10 @@ defaults:
shell: bash shell: bash
jobs: jobs:
# Run the stable test script on the beta channel. Since this branch will soon # Test all samples on the beta channel. Since the beta channel will soon be
# be merged into main as our stable-targeting code, this is the key thing we # promoted to stable, this branch is only concerned with the beta.
# need to test. Beta-CI:
stable-tests-on-beta: name: Test flutter beta channel
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
if: github.repository == 'flutter/samples' if: github.repository == 'flutter/samples'
strategy: strategy:
@ -34,7 +34,7 @@ jobs:
- uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 - uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046
with: with:
channel: beta channel: beta
- run: ./tool/flutter_ci_script_stable.sh - run: flutter pub get && dart tool/ci_script.dart
# Verify the Android add-to-app samples build and pass tests with the beta # Verify the Android add-to-app samples build and pass tests with the beta
# channel. # channel.
@ -54,16 +54,16 @@ jobs:
# Verify the iOS add-to-app samples build and pass tests with the beta # Verify the iOS add-to-app samples build and pass tests with the beta
# channel. # channel.
ios-build: # ios-build:
runs-on: macos-latest # runs-on: macos-latest
if: github.repository == 'flutter/samples' # if: github.repository == 'flutter/samples'
steps: # steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00
with: # with:
distribution: 'zulu' # distribution: 'zulu'
java-version: '17' # java-version: '17'
- uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # - uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046
with: # with:
channel: beta # channel: beta
- run: ./tool/ios_ci_script.sh # - run: ./tool/ios_ci_script.sh

@ -24,7 +24,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
flutter_version: [stable, beta, master] flutter_version: [stable, beta]
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, macos-latest, windows-latest]
steps: steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
@ -35,7 +35,7 @@ jobs:
- uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 - uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046
with: with:
channel: ${{ matrix.flutter_version }} channel: ${{ matrix.flutter_version }}
- run: ./tool/flutter_ci_script_${{ matrix.flutter_version }}.sh - run: flutter pub get && dart tool/ci_script.dart
# android-build: # android-build:
# runs-on: ubuntu-latest # runs-on: ubuntu-latest
@ -51,16 +51,16 @@ jobs:
# channel: stable # channel: stable
# - run: ./tool/android_ci_script.sh # - run: ./tool/android_ci_script.sh
ios-build: # ios-build:
runs-on: macos-latest # runs-on: macos-latest
if: github.repository == 'flutter/samples' # if: github.repository == 'flutter/samples'
steps: # steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00
with: # with:
distribution: 'zulu' # distribution: 'zulu'
java-version: '17' # java-version: '17'
- uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # - uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046
with: # with:
channel: stable # channel: stable
- run: ./tool/ios_ci_script.sh # - run: ./tool/ios_ci_script.sh

4
.gitignore vendored

@ -27,6 +27,7 @@
.pub-cache/ .pub-cache/
.pub/ .pub/
/build/ /build/
**/build/
# Android related # Android related
**/gradle-wrapper.jar **/gradle-wrapper.jar
@ -81,3 +82,6 @@ yarn.lock
!**/ios/**/default.pbxuser !**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3 !**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
.claude/
logs/

@ -1,32 +0,0 @@
# Changelog
The purpose of this changelog is to track the freshness of samples and which
samples reflect *current best practices*. It describes **human-made, significant
** changes made to the repository or samples in the repository.
While all samples in this repository build and run, some of them were written
long ago, and no longer reflect what we want developers to learn. For example,
samples should have been refactored when Dart 3 released to include patterns and
records, where appropriate.
* **DO include:**
* The addition of new samples.
* The removal of existing samples.
* Considerable refactoring of any given sample.
* **DO NOT include:**
* Simple changes that reflect minor version bumps in Flutter. For example,
in a recent Flutter update, `Color.red` became `Color.r`.
* Dependency updates.
* Bug fixes.
* Any changes made to simply 'keep the lights on'.
# Log
| DATE (YYYY-MM-DD) | Sample(s) | author | Changes |
|-------------------|-------------------|--------------|-----------------------------------------------|
| NEXT GOES HERE | | | |
| | | | |
| 2024-12-04 | N/A - repo change | ericwindmill | Added changelog |
| 2024-11-27 | fake_sample | ericwindmill | Refactored fake_sample to use Dart 3 features |
| 2020-04-17 | fake_sample | ericwindmill | Created fake_sample |

@ -7,7 +7,7 @@ A collection of open source samples that illustrate best practices for
## Contributing ## Contributing
We're very appreciative of fixes and necessary improvements to the existing samples. **But in most cases, we're not currently adding new samples to this repository** while we rethink sample code in the post-LLM world. We appreciate fixes and necessary improvements to existing samples. **But in most cases, we're not currently adding new samples to this repository** while we rethink sample code in the new LLM world.
Please read the [contributor's guide] if you have contributions. Please read the [contributor's guide] if you have contributions.

@ -23,7 +23,12 @@ class Cell extends StatefulWidget {
class _CellState extends State<Cell> with WidgetsBindingObserver { class _CellState extends State<Cell> with WidgetsBindingObserver {
static const double gravity = 9.81; static const double gravity = 9.81;
static final AccelerometerEvent defaultPosition = AccelerometerEvent(0, 0, 0); static final AccelerometerEvent defaultPosition = AccelerometerEvent(
0,
0,
0,
DateTime.now(),
);
int cellNumber = 0; int cellNumber = 0;
Random? _random; Random? _random;
@ -82,7 +87,10 @@ class _CellState extends State<Cell> with WidgetsBindingObserver {
builder: (context) { builder: (context) {
return Card( return Card(
// Mimic the platform Material look. // Mimic the platform Material look.
margin: const EdgeInsets.symmetric(horizontal: 36, vertical: 24), margin: const EdgeInsets.symmetric(
horizontal: 36,
vertical: 24,
),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
), ),
@ -112,22 +120,22 @@ class _CellState extends State<Cell> with WidgetsBindingObserver {
child: StreamBuilder<AccelerometerEvent>( child: StreamBuilder<AccelerometerEvent>(
// Don't continuously rebuild for nothing when the // Don't continuously rebuild for nothing when the
// cell isn't visible. // cell isn't visible.
stream: stream: appLifecycleState == AppLifecycleState.resumed
appLifecycleState == AppLifecycleState.resumed ? accelerometerEventStream()
? accelerometerEventStream() : Stream.value(defaultPosition),
: Stream.value(defaultPosition),
initialData: defaultPosition, initialData: defaultPosition,
builder: (context, snapshot) { builder: (context, snapshot) {
return Transform( return Transform(
// Figure out the phone's orientation relative // Figure out the phone's orientation relative
// to gravity's direction. Ignore the z vector. // to gravity's direction. Ignore the z vector.
transform: Matrix4.rotationX( transform:
snapshot.data!.y / gravity * pi / 2, Matrix4.rotationX(
)..multiply( snapshot.data!.y / gravity * pi / 2,
Matrix4.rotationY( )..multiply(
snapshot.data!.x / gravity * pi / 2, Matrix4.rotationY(
), snapshot.data!.x / gravity * pi / 2,
), ),
),
alignment: Alignment.center, alignment: Alignment.center,
child: const FlutterLogo(size: 72), child: const FlutterLogo(size: 72),
); );

@ -1,17 +1,17 @@
name: flutter_module_using_plugin name: flutter_module_using_plugin_android_view
description: An example Flutter module that uses a plugin. description: An example Flutter module that uses a plugin.
version: 1.0.0+1 version: 1.0.0+1
resolution: workspace
environment: environment:
sdk: ^3.7.0-0 sdk: ^3.9.0-0
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
provider: ^6.0.2 provider: ^6.1.5
url_launcher: ^6.0.20 url_launcher: ^6.3.2
sensors_plus: ^5.0.1 sensors_plus: ^6.1.1
dev_dependencies: dev_dependencies:
analysis_defaults: analysis_defaults:

@ -6,7 +6,7 @@
// tree, read text, and verify that the values of widget properties are correct. // tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_module_using_plugin/main.dart'; import 'package:flutter_module_using_plugin_android_view/main.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';

@ -125,28 +125,26 @@ class _BookDetailState extends State<BookDetail> {
IconButton( IconButton(
icon: const Icon(Icons.check), icon: const Icon(Icons.check),
// Pressing save sends the updated book to the platform. // Pressing save sends the updated book to the platform.
onPressed: onPressed: book != null
book != null ? () {
? () { hostApi.finishEditingBook(book!);
hostApi.finishEditingBook(book!); clear();
clear(); }
} : null,
: null,
), ),
], ],
), ),
body: body: book == null
book == null // Draw a spinner until the platform gives us the book to show details
// Draw a spinner until the platform gives us the book to show details // for.
// for. ? const Center(child: CircularProgressIndicator())
? const Center(child: CircularProgressIndicator()) : BookForm(
: BookForm( book: book!,
book: book!, focusNode: textFocusNode,
focusNode: textFocusNode, authorTextController: authorTextController,
authorTextController: authorTextController, subtitleTextController: subtitleTextController,
subtitleTextController: subtitleTextController, titleTextController: titleTextController,
titleTextController: titleTextController, ),
),
); );
} }
} }

@ -2,11 +2,11 @@ name: flutter_module_books
description: A Flutter module using the Pigeon package to demonstrate description: A Flutter module using the Pigeon package to demonstrate
integrating Flutter in a realistic scenario where the existing platform app integrating Flutter in a realistic scenario where the existing platform app
already has business logic and middleware constraints. already has business logic and middleware constraints.
version: 1.0.0+1 version: 1.0.0+1
resolution: workspace
environment: environment:
sdk: ^3.7.0-0 sdk: ^3.9.0-0
dependencies: dependencies:
flutter: flutter:

@ -20,11 +20,15 @@ void main() {
expect(mockHostApi.cancelCalls, 1); expect(mockHostApi.cancelCalls, 1);
}); });
testWidgets('Pressing done calls the finish editing API', (tester) async { testWidgets('Pressing done calls the finish editing API', (
tester,
) async {
MockHostBookApi mockHostApi = MockHostBookApi(); MockHostBookApi mockHostApi = MockHostBookApi();
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp(home: BookDetail(book: Book(), hostApi: mockHostApi)), MaterialApp(
home: BookDetail(book: Book(), hostApi: mockHostApi),
),
); );
await tester.tap(find.byIcon(Icons.check)); await tester.tap(find.byIcon(Icons.check));

@ -1,10 +1,10 @@
name: flutter_module name: flutter_module_fullscreen
description: An example Flutter module. description: An example Flutter module.
version: 1.0.0+1 version: 1.0.0+1
resolution: workspace
environment: environment:
sdk: ^3.7.0-0 sdk: ^3.9.0-0
dependencies: dependencies:
flutter: flutter:

@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_module/main.dart'; import 'package:flutter_module_fullscreen/main.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';

@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter_driver/driver_extension.dart'; import 'package:flutter_driver/driver_extension.dart';
import 'package:flutter_module/main.dart' as app; import 'package:flutter_module_fullscreen/main.dart' as app;
// This alternate entrypoint is used for espresso testing. See // This alternate entrypoint is used for espresso testing. See
// https://pub.dev/packages/espresso for details. // https://pub.dev/packages/espresso for details.

@ -82,7 +82,10 @@ class _MyHomePageState extends State<MyHomePage> {
'$_counter', '$_counter',
style: Theme.of(context).textTheme.headlineMedium, style: Theme.of(context).textTheme.headlineMedium,
), ),
TextButton(onPressed: _incrementCounter, child: const Text('Add')), TextButton(
onPressed: _incrementCounter,
child: const Text('Add'),
),
TextButton( TextButton(
onPressed: () { onPressed: () {
_channel.invokeMethod<void>("next", _counter); _channel.invokeMethod<void>("next", _counter);

@ -1,10 +1,10 @@
name: multiple_flutters_module name: multiple_flutters_module
description: A module that is embedded in the multiple_flutters_ios and multiple_flutters_android sample code. description: A module that is embedded in the multiple_flutters_ios and multiple_flutters_android sample code.
version: 1.0.0+1 version: 1.0.0+1
resolution: workspace
environment: environment:
sdk: ^3.7.0-0 sdk: ^3.9.0-0
dependencies: dependencies:
flutter: flutter:

@ -23,7 +23,12 @@ class Cell extends StatefulWidget {
class _CellState extends State<Cell> with WidgetsBindingObserver { class _CellState extends State<Cell> with WidgetsBindingObserver {
static const double gravity = 9.81; static const double gravity = 9.81;
static final AccelerometerEvent defaultPosition = AccelerometerEvent(0, 0, 0); static final AccelerometerEvent defaultPosition = AccelerometerEvent(
0,
0,
0,
DateTime.now(),
);
int cellNumber = 0; int cellNumber = 0;
Random? _random; Random? _random;
@ -82,7 +87,10 @@ class _CellState extends State<Cell> with WidgetsBindingObserver {
builder: (context) { builder: (context) {
return Card( return Card(
// Mimic the platform Material look. // Mimic the platform Material look.
margin: const EdgeInsets.symmetric(horizontal: 36, vertical: 24), margin: const EdgeInsets.symmetric(
horizontal: 36,
vertical: 24,
),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
), ),
@ -112,10 +120,9 @@ class _CellState extends State<Cell> with WidgetsBindingObserver {
child: StreamBuilder<AccelerometerEvent>( child: StreamBuilder<AccelerometerEvent>(
// Don't continuously rebuild for nothing when the // Don't continuously rebuild for nothing when the
// cell isn't visible. // cell isn't visible.
stream: stream: appLifecycleState == AppLifecycleState.resumed
appLifecycleState == AppLifecycleState.resumed ? accelerometerEventStream()
? accelerometerEventStream() : Stream.value(defaultPosition),
: Stream.value(defaultPosition),
initialData: defaultPosition, initialData: defaultPosition,
builder: (context, snapshot) { builder: (context, snapshot) {
final data = snapshot.data; final data = snapshot.data;
@ -125,11 +132,14 @@ class _CellState extends State<Cell> with WidgetsBindingObserver {
return Transform( return Transform(
// Figure out the phone's orientation relative // Figure out the phone's orientation relative
// to gravity's direction. Ignore the z vector. // to gravity's direction. Ignore the z vector.
transform: Matrix4.rotationX( transform:
data.y / gravity * pi / 2, Matrix4.rotationX(
)..multiply( data.y / gravity * pi / 2,
Matrix4.rotationY(data.x / gravity * pi / 2), )..multiply(
), Matrix4.rotationY(
data.x / gravity * pi / 2,
),
),
alignment: Alignment.center, alignment: Alignment.center,
child: const FlutterLogo(size: 72), child: const FlutterLogo(size: 72),
); );

@ -1,17 +1,17 @@
name: flutter_module_using_plugin name: flutter_module_using_plugin
description: An example Flutter module that uses a plugin. description: An example Flutter module that uses a plugin.
version: 1.0.0+1 version: 1.0.0+1
resolution: workspace
environment: environment:
sdk: ^3.7.0-0 sdk: ^3.9.0-0
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
provider: ^6.0.2 provider: ^6.0.2
url_launcher: ^6.0.20 url_launcher: ^6.0.20
sensors_plus: ^5.0.1 sensors_plus: ^6.1.1
dev_dependencies: dev_dependencies:
analysis_defaults: analysis_defaults:

@ -1,10 +1,10 @@
name: flutter_module name: flutter_module
description: An example Flutter module. description: An example Flutter module.
version: 1.0.0+1 version: 1.0.0+1
resolution: workspace
environment: environment:
sdk: ^3.7.0-0 sdk: ^3.9.0-0
dependencies: dependencies:
flutter: flutter:

@ -1,3 +1,4 @@
# https://dart.dev/guides/libraries/private-files # https://dart.dev/guides/libraries/private-files
# Created by `dart pub` # Created by `dart pub`
.dart_tool/ .dart_tool/
.build/

@ -1,5 +1,9 @@
include: package:flutter_lints/flutter.yaml include: package:flutter_lints/flutter.yaml
formatter:
trailing_commas: preserve
page_width: 79
analyzer: analyzer:
language: language:
strict-casts: true strict-casts: true

@ -1,11 +1,14 @@
name: analysis_defaults name: analysis_defaults
description: Analysis defaults for flutter/samples description: Analysis defaults for flutter/samples
publish_to: none publish_to: none
resolution: workspace
environment: environment:
sdk: ^3.7.0-0 sdk: ^3.9.0-0
# NOTE: Code is not allowed in this package. Do not add more dependencies. # NOTE: Code is not allowed in this package. Do not add more dependencies.
# The `flutter_lints` dependency is required for `lib/flutter.yaml`. # The `flutter_lints` dependency is required for `lib/flutter.yaml`.
dependencies: dependencies:
flutter_lints: ^5.0.0 flutter_lints: ^6.0.0

@ -1,23 +1,20 @@
name: splash_screen_sample name: splash_screen_sample
description: A sample Flutter app with animated splash screen on Android 12. description: A sample Flutter app with animated splash screen on Android 12.
publish_to: "none" publish_to: "none"
version: 1.0.0+1 version: 1.0.0+1
resolution: workspace
environment: environment:
sdk: ^3.7.0-0 sdk: ^3.9.0-0
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
dev_dependencies: dev_dependencies:
analysis_defaults: analysis_defaults:
path: ../analysis_defaults path: ../analysis_defaults
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter: flutter:
uses-material-design: true uses-material-design: true
assets: assets:

@ -21,7 +21,8 @@ const double windowWidth = 480;
const double windowHeight = 854; const double windowHeight = 854;
void setupWindow() { void setupWindow() {
if (!kIsWeb && (Platform.isWindows || Platform.isLinux || Platform.isMacOS)) { if (!kIsWeb &&
(Platform.isWindows || Platform.isLinux || Platform.isMacOS)) {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
setWindowTitle('Animation Samples'); setWindowTitle('Animation Samples');
setWindowMinSize(const Size(windowWidth, windowHeight)); setWindowMinSize(const Size(windowWidth, windowHeight));
@ -43,7 +44,11 @@ class Demo {
final String route; final String route;
final WidgetBuilder builder; final WidgetBuilder builder;
const Demo({required this.name, required this.route, required this.builder}); const Demo({
required this.name,
required this.route,
required this.builder,
});
} }
final basicDemos = [ final basicDemos = [

@ -49,7 +49,9 @@ class _AnimatedBuilderDemoState extends State<AnimatedBuilderDemo>
animation: animation, animation: animation,
builder: (context, child) { builder: (context, child) {
return ElevatedButton( return ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: animation.value), style: ElevatedButton.styleFrom(
backgroundColor: animation.value,
),
child: child, child: child,
onPressed: () { onPressed: () {
switch (controller.status) { switch (controller.status) {

@ -49,18 +49,21 @@ class _FadeTransitionDemoState extends State<FadeTransitionDemo>
children: [ children: [
FadeTransition( FadeTransition(
opacity: _animation, opacity: _animation,
child: const Icon(Icons.star, color: Colors.amber, size: 300), child: const Icon(
Icons.star,
color: Colors.amber,
size: 300,
),
), ),
ElevatedButton( ElevatedButton(
child: const Text('animate'), child: const Text('animate'),
onPressed: onPressed: () => setState(() {
() => setState(() { _controller
_controller .animateTo(1.0)
.animateTo(1.0) .then<TickerFuture>(
.then<TickerFuture>( (value) => _controller.animateBack(0.0),
(value) => _controller.animateBack(0.0), );
); }),
}),
), ),
], ],
), ),

@ -74,7 +74,10 @@ class _TweenSequenceDemoState extends State<TweenSequenceDemo>
child: child, child: child,
); );
}, },
child: const Text('Animate', style: TextStyle(color: Colors.white)), child: const Text(
'Animate',
style: TextStyle(color: Colors.white),
),
), ),
), ),
); );

@ -75,7 +75,9 @@ class _AnimatedListDemoState extends State<AnimatedListDemo> {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('AnimatedList'), title: const Text('AnimatedList'),
actions: [IconButton(icon: const Icon(Icons.add), onPressed: addUser)], actions: [
IconButton(icon: const Icon(Icons.add), onPressed: addUser),
],
), ),
body: SafeArea( body: SafeArea(
child: AnimatedList( child: AnimatedList(

@ -11,7 +11,8 @@ class AnimatedPositionedDemo extends StatefulWidget {
static String routeName = 'misc/animated_positioned'; static String routeName = 'misc/animated_positioned';
@override @override
State<AnimatedPositionedDemo> createState() => _AnimatedPositionedDemoState(); State<AnimatedPositionedDemo> createState() =>
_AnimatedPositionedDemoState();
} }
class _AnimatedPositionedDemoState extends State<AnimatedPositionedDemo> { class _AnimatedPositionedDemoState extends State<AnimatedPositionedDemo> {
@ -54,12 +55,11 @@ class _AnimatedPositionedDemoState extends State<AnimatedPositionedDemo> {
left: leftPosition, left: leftPosition,
duration: const Duration(seconds: 1), duration: const Duration(seconds: 1),
child: InkWell( child: InkWell(
onTap: onTap: () => changePosition(
() => changePosition( size.height -
size.height - (appBar.preferredSize.height + topPadding + 50),
(appBar.preferredSize.height + topPadding + 50), size.width - 150,
size.width - 150, ),
),
child: Container( child: Container(
alignment: Alignment.center, alignment: Alignment.center,
width: 150, width: 150,
@ -68,8 +68,9 @@ class _AnimatedPositionedDemoState extends State<AnimatedPositionedDemo> {
child: Text( child: Text(
'Click Me', 'Click Me',
style: TextStyle( style: TextStyle(
color: color: Theme.of(
Theme.of(context).buttonTheme.colorScheme!.onPrimary, context,
).buttonTheme.colorScheme!.onPrimary,
), ),
), ),
), ),

@ -48,8 +48,8 @@ class _AnimatedSwitcherDemoState extends State<AnimatedSwitcherDemo> {
title: const Text('AnimatedSwitcher'), title: const Text('AnimatedSwitcher'),
actions: [ actions: [
TextButton( TextButton(
onPressed: onPressed: () =>
() => setState(() => container = generateContainer(++keyCount)), setState(() => container = generateContainer(++keyCount)),
child: const Text('Change Widget'), child: const Text('Change Widget'),
), ),
], ],
@ -61,9 +61,8 @@ class _AnimatedSwitcherDemoState extends State<AnimatedSwitcherDemo> {
child: AnimatedSwitcher( child: AnimatedSwitcher(
duration: const Duration(seconds: 1), duration: const Duration(seconds: 1),
child: container, child: container,
transitionBuilder: transitionBuilder: (child, animation) =>
(child, animation) => ScaleTransition(scale: animation, child: child),
ScaleTransition(scale: animation, child: child),
), ),
), ),
); );

@ -15,8 +15,9 @@ class CarouselDemo extends StatelessWidget {
'assets/eat_sydney_sm.jpg', 'assets/eat_sydney_sm.jpg',
]; ];
final List<Widget> images = final List<Widget> images = fileNames
fileNames.map((file) => Image.asset(file, fit: BoxFit.cover)).toList(); .map((file) => Image.asset(file, fit: BoxFit.cover))
.toList();
@override @override
Widget build(context) { Widget build(context) {
@ -77,29 +78,32 @@ class _CarouselState extends State<Carousel> {
}, },
controller: _controller, controller: _controller,
scrollBehavior: ScrollConfiguration.of(context).copyWith( scrollBehavior: ScrollConfiguration.of(context).copyWith(
dragDevices: {ui.PointerDeviceKind.touch, ui.PointerDeviceKind.mouse}, dragDevices: {
ui.PointerDeviceKind.touch,
ui.PointerDeviceKind.mouse,
},
),
itemBuilder: (context, index) => AnimatedBuilder(
animation: _controller,
builder: (context, child) {
var result = _pageHasChanged
? _controller.page!
: _currentPage * 1.0;
// The horizontal position of the page between a 1 and 0
var value = result - index;
value = (1 - (value.abs() * .5)).clamp(0.0, 1.0);
return Center(
child: SizedBox(
height: Curves.easeOut.transform(value) * size.height,
width: Curves.easeOut.transform(value) * size.width,
child: child,
),
);
},
child: widget.itemBuilder(context, index),
), ),
itemBuilder:
(context, index) => AnimatedBuilder(
animation: _controller,
builder: (context, child) {
var result =
_pageHasChanged ? _controller.page! : _currentPage * 1.0;
// The horizontal position of the page between a 1 and 0
var value = result - index;
value = (1 - (value.abs() * .5)).clamp(0.0, 1.0);
return Center(
child: SizedBox(
height: Curves.easeOut.transform(value) * size.height,
width: Curves.easeOut.transform(value) * size.width,
child: child,
),
);
},
child: widget.itemBuilder(context, index),
),
); );
} }

@ -91,13 +91,12 @@ class _CurvedAnimationDemoState extends State<CurvedAnimationDemo>
style: Theme.of(context).textTheme.titleLarge, style: Theme.of(context).textTheme.titleLarge,
), ),
DropdownButton<CurveChoice>( DropdownButton<CurveChoice>(
items: items: curves.map((curve) {
curves.map((curve) { return DropdownMenuItem<CurveChoice>(
return DropdownMenuItem<CurveChoice>( value: curve,
value: curve, child: Text(curve.name),
child: Text(curve.name), );
); }).toList(),
}).toList(),
onChanged: (newCurve) { onChanged: (newCurve) {
if (newCurve != null) { if (newCurve != null) {
setState(() { setState(() {
@ -114,13 +113,12 @@ class _CurvedAnimationDemoState extends State<CurvedAnimationDemo>
style: Theme.of(context).textTheme.titleLarge, style: Theme.of(context).textTheme.titleLarge,
), ),
DropdownButton<CurveChoice>( DropdownButton<CurveChoice>(
items: items: curves.map((curve) {
curves.map((curve) { return DropdownMenuItem<CurveChoice>(
return DropdownMenuItem<CurveChoice>( value: curve,
value: curve, child: Text(curve.name),
child: Text(curve.name), );
); }).toList(),
}).toList(),
onChanged: (newCurve) { onChanged: (newCurve) {
if (newCurve != null) { if (newCurve != null) {
setState(() { setState(() {

@ -52,26 +52,24 @@ class _ExpandCardState extends State<ExpandCard>
duration: duration, duration: duration,
firstCurve: Curves.easeInOutCubic, firstCurve: Curves.easeInOutCubic,
secondCurve: Curves.easeInOutCubic, secondCurve: Curves.easeInOutCubic,
crossFadeState: crossFadeState: selected
selected ? CrossFadeState.showSecond
? CrossFadeState.showSecond : CrossFadeState.showFirst,
: CrossFadeState.showFirst,
// Use Positioned.fill() to pass the constraints to its children. // Use Positioned.fill() to pass the constraints to its children.
// This allows the Images to use BoxFit.cover to cover the correct // This allows the Images to use BoxFit.cover to cover the correct
// size // size
layoutBuilder: ( layoutBuilder:
topChild, (topChild, topChildKey, bottomChild, bottomChildKey) {
topChildKey, return Stack(
bottomChild, children: [
bottomChildKey, Positioned.fill(
) { key: bottomChildKey,
return Stack( child: bottomChild,
children: [ ),
Positioned.fill(key: bottomChildKey, child: bottomChild), Positioned.fill(key: topChildKey, child: topChild),
Positioned.fill(key: topChildKey, child: topChild), ],
], );
); },
},
firstChild: Image.asset( firstChild: Image.asset(
'assets/eat_cape_town_sm.jpg', 'assets/eat_cape_town_sm.jpg',
fit: BoxFit.cover, fit: BoxFit.cover,

@ -18,20 +18,21 @@ class FlutterAnimateDemo extends StatelessWidget {
body: Center( body: Center(
child: Padding( child: Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Text( child:
"Hello Flutter Animate", Text(
style: Theme.of(context).textTheme.headlineLarge, "Hello Flutter Animate",
) style: Theme.of(context).textTheme.headlineLarge,
.animate(onPlay: (controller) => controller.repeat()) )
.then(delay: 250.ms) .animate(onPlay: (controller) => controller.repeat())
.fadeIn(duration: 500.ms) .then(delay: 250.ms)
.then(delay: 250.ms) .fadeIn(duration: 500.ms)
.shimmer(duration: 400.ms) .then(delay: 250.ms)
.then(delay: 250.ms) .shimmer(duration: 400.ms)
.slide() .then(delay: 250.ms)
.then(delay: 250.ms) .slide()
.blur(duration: 500.ms) .then(delay: 250.ms)
.then(delay: 100.ms), .blur(duration: 500.ms)
.then(delay: 100.ms),
), ),
), ),
); );

@ -29,10 +29,12 @@ class Grid extends StatelessWidget {
), ),
itemBuilder: (context, index) { itemBuilder: (context, index) {
return (index >= 20) return (index >= 20)
? const SmallCard(imageAssetName: 'assets/eat_cape_town_sm.jpg') ? const SmallCard(
imageAssetName: 'assets/eat_cape_town_sm.jpg',
)
: const SmallCard( : const SmallCard(
imageAssetName: 'assets/eat_new_orleans_sm.jpg', imageAssetName: 'assets/eat_new_orleans_sm.jpg',
); );
}, },
), ),
); );
@ -50,7 +52,9 @@ Route _createRoute(BuildContext parentContext, String image) {
).chain(CurveTween(curve: Curves.ease)).animate(animation); ).chain(CurveTween(curve: Curves.ease)).animate(animation);
return Stack( return Stack(
children: [PositionedTransition(rect: rectAnimation, child: child)], children: [
PositionedTransition(rect: rectAnimation, child: child),
],
); );
}, },
); );

@ -15,10 +15,15 @@ class HeroAnimationDemo extends StatelessWidget {
body: GestureDetector( body: GestureDetector(
child: Hero( child: Hero(
tag: 'hero-page-child', tag: 'hero-page-child',
child: _createHeroContainer(size: 50.0, color: Colors.grey.shade300), child: _createHeroContainer(
size: 50.0,
color: Colors.grey.shade300,
),
), ),
onTap: onTap: () =>
() => Navigator.of(context).push<void>( Navigator.of(
context,
).push<void>(
MaterialPageRoute(builder: (context) => const HeroPage()), MaterialPageRoute(builder: (context) => const HeroPage()),
), ),
), ),
@ -52,7 +57,9 @@ StatelessWidget _createHeroContainer({
height: size, height: size,
width: size, width: size,
padding: const EdgeInsets.all(10.0), padding: const EdgeInsets.all(10.0),
margin: size < 100.0 ? const EdgeInsets.all(10.0) : const EdgeInsets.all(0), margin: size < 100.0
? const EdgeInsets.all(10.0)
: const EdgeInsets.all(0),
decoration: BoxDecoration(shape: BoxShape.circle, color: color), decoration: BoxDecoration(shape: BoxShape.circle, color: color),
child: const FlutterLogo(), child: const FlutterLogo(),
); );

@ -78,7 +78,9 @@ class _DraggableCardState extends State<DraggableCard>
void initState() { void initState() {
super.initState(); super.initState();
_controller = AnimationController.unbounded(vsync: this) _controller = AnimationController.unbounded(vsync: this)
..addListener(() => setState(() => _dragAlignment = _animation.value)); ..addListener(
() => setState(() => _dragAlignment = _animation.value),
);
} }
@override @override
@ -92,17 +94,18 @@ class _DraggableCardState extends State<DraggableCard>
final size = MediaQuery.of(context).size; final size = MediaQuery.of(context).size;
return GestureDetector( return GestureDetector(
onPanStart: (details) => _controller.stop(canceled: true), onPanStart: (details) => _controller.stop(canceled: true),
onPanUpdate: onPanUpdate: (details) => setState(
(details) => setState( () => _dragAlignment += Alignment(
() => details.delta.dx / (size.width / 2),
_dragAlignment += Alignment( details.delta.dy / (size.height / 2),
details.delta.dx / (size.width / 2), ),
details.delta.dy / (size.height / 2), ),
), onPanEnd: (details) =>
), _runAnimation(details.velocity.pixelsPerSecond, size),
onPanEnd: child: Align(
(details) => _runAnimation(details.velocity.pixelsPerSecond, size), alignment: _dragAlignment,
child: Align(alignment: _dragAlignment, child: Card(child: widget.child)), child: Card(child: widget.child),
),
); );
} }
} }

@ -9,7 +9,8 @@ class RepeatingAnimationDemo extends StatefulWidget {
static String routeName = 'misc/repeating_animation'; static String routeName = 'misc/repeating_animation';
@override @override
State<RepeatingAnimationDemo> createState() => _RepeatingAnimationDemoState(); State<RepeatingAnimationDemo> createState() =>
_RepeatingAnimationDemoState();
} }
class _RepeatingAnimationDemoState extends State<RepeatingAnimationDemo> class _RepeatingAnimationDemoState extends State<RepeatingAnimationDemo>

@ -2,15 +2,16 @@ name: animations
description: A new Flutter project. description: A new Flutter project.
version: 1.0.0+1 version: 1.0.0+1
publish_to: none publish_to: none
resolution: workspace
environment: environment:
sdk: ^3.7.0-0 sdk: ^3.9.0-0
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_animate: ^4.1.0 flutter_animate: ^4.1.0
go_router: ^16.1.0 go_router: ^16.0.0
window_size: # plugin is not yet part of the flutter framework window_size: # plugin is not yet part of the flutter framework
git: git:
url: https://github.com/google/flutter-desktop-embedding.git url: https://github.com/google/flutter-desktop-embedding.git
@ -22,7 +23,6 @@ dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter: flutter:
uses-material-design: true uses-material-design: true
assets: assets:

@ -16,7 +16,8 @@ void main() {
// Get the initial color of the button. // Get the initial color of the button.
ElevatedButton button = tester.widget(find.byType(ElevatedButton)); ElevatedButton button = tester.widget(find.byType(ElevatedButton));
WidgetStateProperty<Color?>? initialColor = button.style!.backgroundColor; WidgetStateProperty<Color?>? initialColor =
button.style!.backgroundColor;
// Tap the button. // Tap the button.
await tester.tap(find.byType(ElevatedButton)); await tester.tap(find.byType(ElevatedButton));
@ -24,7 +25,8 @@ void main() {
// Get the updated color of the button. // Get the updated color of the button.
button = tester.widget(find.byType(ElevatedButton)); button = tester.widget(find.byType(ElevatedButton));
WidgetStateProperty<Color?>? updatedColor = button.style!.backgroundColor; WidgetStateProperty<Color?>? updatedColor =
button.style!.backgroundColor;
// Check if the color has changed. // Check if the color has changed.
expect(initialColor, isNot(updatedColor)); expect(initialColor, isNot(updatedColor));
@ -35,7 +37,8 @@ void main() {
// Get the initial color of the button. // Get the initial color of the button.
ElevatedButton button = tester.widget(find.byType(ElevatedButton)); ElevatedButton button = tester.widget(find.byType(ElevatedButton));
WidgetStateProperty<Color?>? initialColor = button.style!.backgroundColor; WidgetStateProperty<Color?>? initialColor =
button.style!.backgroundColor;
// Tap the button to trigger the animation but don't wait for it to finish. // Tap the button to trigger the animation but don't wait for it to finish.
await tester.tap(find.byType(ElevatedButton)); await tester.tap(find.byType(ElevatedButton));
@ -44,7 +47,8 @@ void main() {
// Check that the color has changed but not to the final color. // Check that the color has changed but not to the final color.
button = tester.widget(find.byType(ElevatedButton)); button = tester.widget(find.byType(ElevatedButton));
WidgetStateProperty<Color?>? changedColor = button.style!.backgroundColor; WidgetStateProperty<Color?>? changedColor =
button.style!.backgroundColor;
expect(initialColor, isNot(changedColor)); expect(initialColor, isNot(changedColor));
// Wait for the animation to finish. // Wait for the animation to finish.

@ -24,7 +24,10 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Check if removed properly. // Check if removed properly.
expect(tester.widgetList(find.byType(Card)).length, lessThan(totalCards)); expect(
tester.widgetList(find.byType(Card)).length,
lessThan(totalCards),
);
}); });
testWidgets('All cards swiped out', (tester) async { testWidgets('All cards swiped out', (tester) async {
@ -36,7 +39,10 @@ void main() {
// Swipe out all cards. // Swipe out all cards.
for (var i = 0; i < totalCards; i++) { for (var i = 0; i < totalCards; i++) {
// Swipe out one by one. // Swipe out one by one.
await tester.drag(find.byType(Card).last, const Offset(100.0, 0.0)); await tester.drag(
find.byType(Card).last,
const Offset(100.0, 0.0),
);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
} }

@ -18,7 +18,11 @@ void main() {
expect(imageList.length, 2); expect(imageList.length, 2);
// Swipe the Carousel. // Swipe the Carousel.
await tester.fling(find.byType(CarouselDemo), const Offset(-400, 0), 800); await tester.fling(
find.byType(CarouselDemo),
const Offset(-400, 0),
800,
);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Get the images available on the screen after swipe. // Get the images available on the screen after swipe.

@ -22,7 +22,10 @@ void main() {
// The size of ExpandCard must change once tapped. // The size of ExpandCard must change once tapped.
// The initialSize should be less than current ExpandCard size. // The initialSize should be less than current ExpandCard size.
expect(initialSize, lessThan(tester.getSize(find.byType(ExpandCard)))); expect(
initialSize,
lessThan(tester.getSize(find.byType(ExpandCard))),
);
}); });
testWidgets('ExpandCard changes image on tap', (tester) async { testWidgets('ExpandCard changes image on tap', (tester) async {

@ -33,7 +33,9 @@ void main() {
expect(finalSize, greaterThan(initialSize)); expect(finalSize, greaterThan(initialSize));
}); });
testWidgets('Final inkwell on tap goes back to the grid', (tester) async { testWidgets('Final inkwell on tap goes back to the grid', (
tester,
) async {
await tester.pumpWidget(createFocusImageScreen()); await tester.pumpWidget(createFocusImageScreen());
// Tap on the ink well at index 0. // Tap on the ink well at index 0.

@ -58,7 +58,9 @@ void main() {
// Final color should not be same as initial color. // Final color should not be same as initial color.
expect( expect(
(finalContainer.decoration as BoxDecoration).color, (finalContainer.decoration as BoxDecoration).color,
isNot(equals((initialContainer.decoration as BoxDecoration).color)), isNot(
equals((initialContainer.decoration as BoxDecoration).color),
),
); );
}); });
@ -66,7 +68,9 @@ void main() {
await tester.pumpWidget(createHeroAnimationDemoScreen()); await tester.pumpWidget(createHeroAnimationDemoScreen());
// Get the initial Screen. // Get the initial Screen.
final initialScreen = tester.firstWidget(find.byType(HeroAnimationDemo)); final initialScreen = tester.firstWidget(
find.byType(HeroAnimationDemo),
);
// Tap on the GestureDetector. // Tap on the GestureDetector.
await tester.tap(find.byType(GestureDetector)); await tester.tap(find.byType(GestureDetector));

@ -1 +0,0 @@
include: package:flutter_lints/flutter.yaml

@ -11,10 +11,9 @@ int main(List<String> arguments) {
// the `--input` option and one for the `--output` option. // the `--input` option and one for the `--output` option.
// `--input` is the original asset file that this program should transform. // `--input` is the original asset file that this program should transform.
// `--output` is where flutter expects the transformation output to be written to. // `--output` is where flutter expects the transformation output to be written to.
final parser = final parser = ArgParser()
ArgParser() ..addOption(inputOptionName, mandatory: true, abbr: 'i')
..addOption(inputOptionName, mandatory: true, abbr: 'i') ..addOption(outputOptionName, mandatory: true, abbr: 'o');
..addOption(outputOptionName, mandatory: true, abbr: 'o');
ArgResults argResults = parser.parse(arguments); ArgResults argResults = parser.parse(arguments);
final String inputFilePath = argResults[inputOptionName]; final String inputFilePath = argResults[inputOptionName];

@ -3,7 +3,7 @@ description: A sample command-line application.
version: 1.0.0 version: 1.0.0
environment: environment:
sdk: ^3.7.0-0 sdk: ^3.9.0-0
dependencies: dependencies:
args: ^2.4.2 args: ^2.4.2

@ -2,9 +2,10 @@ name: asset_transformation
description: "A new Flutter project." description: "A new Flutter project."
publish_to: 'none' publish_to: 'none'
version: 0.1.0 version: 0.1.0
resolution: workspace
environment: environment:
sdk: ^3.7.0-0 sdk: ^3.9.0-0
dependencies: dependencies:
flutter: flutter:
@ -12,9 +13,11 @@ dependencies:
vector_graphics: ^1.1.11+1 vector_graphics: ^1.1.11+1
dev_dependencies: dev_dependencies:
analysis_defaults:
path: ../analysis_defaults
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^5.0.0 flutter_lints: ^6.0.0
vector_graphics_compiler: ^1.1.11+1 vector_graphics_compiler: ^1.1.11+1
grayscale_transformer: grayscale_transformer:
path: ./grayscale_transformer path: ./grayscale_transformer

@ -69,8 +69,8 @@ class _MyHomePageState extends State<MyHomePage> {
.then( .then(
(sharedPreferences) => sharedPreferences.setBool('isDebug', true), (sharedPreferences) => sharedPreferences.setBool('isDebug', true),
); );
final Future<Directory> tempDirFuture = final Future<Directory> tempDirFuture = path_provider
path_provider.getTemporaryDirectory(); .getTemporaryDirectory();
// Wait until the [SharedPreferences] value is set and the temporary // Wait until the [SharedPreferences] value is set and the temporary
// directory is received before opening the database. If // directory is received before opening the database. If
@ -130,8 +130,9 @@ class _MyHomePageState extends State<MyHomePage> {
padding: const EdgeInsets.symmetric(horizontal: 8.0), padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: SearchBar( child: SearchBar(
hintText: 'Search', hintText: 'Search',
onChanged: onChanged: _database == null
_database == null ? null : (query) => _refresh(query: query), ? null
: (query) => _refresh(query: query),
trailing: const [Icon(Icons.search), SizedBox(width: 8)], trailing: const [Icon(Icons.search), SizedBox(width: 8)],
), ),
), ),

@ -188,7 +188,9 @@ class _SimpleDatabaseServer {
// [BinaryMessenger] that the Platform Channels will communicate with on // [BinaryMessenger] that the Platform Channels will communicate with on
// the background isolate. // the background isolate.
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken); BackgroundIsolateBinaryMessenger.ensureInitialized(
rootIsolateToken,
);
_sendPort.send(const _Command(_Codes.ack, arg0: null)); _sendPort.send(const _Command(_Codes.ack, arg0: null));
case _Codes.add: case _Codes.add:
_doAddEntry(command.arg0 as String); _doAddEntry(command.arg0 as String);

@ -1,12 +1,13 @@
name: background_isolate_channels name: background_isolate_channels
description: A new Flutter project. description: A new Flutter project.
resolution: workspace
publish_to: 'none' # Remove this line if you wish to publish to pub.dev publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1 version: 1.0.0+1
environment: environment:
sdk: ^3.7.0-0 sdk: ^3.9.0-0
dependencies: dependencies:
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
@ -23,6 +24,5 @@ dev_dependencies:
path: ../analysis_defaults path: ../analysis_defaults
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter: flutter:
uses-material-design: true uses-material-design: true

@ -2,9 +2,10 @@ name: client
description: A Flutter app which communicates with a Dart backend using shared business logic. description: A Flutter app which communicates with a Dart backend using shared business logic.
publish_to: "none" publish_to: "none"
version: 1.0.0+1 version: 1.0.0+1
resolution: workspace
environment: environment:
sdk: ^3.7.0-0 sdk: ^3.9.0-0
dependencies: dependencies:
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
@ -15,7 +16,9 @@ dependencies:
path: ../shared path: ../shared
dev_dependencies: dev_dependencies:
flutter_lints: ^5.0.0 analysis_defaults:
path: ../../analysis_defaults
flutter_lints: ^6.0.0
flutter_test: flutter_test:
sdk: flutter sdk: flutter

@ -9,10 +9,9 @@ import 'package:shelf_router/shelf_router.dart';
int count = 0; int count = 0;
// Configure routes. // Configure routes.
final _router = final _router = Router()
Router() ..post('/', _incrementHandler)
..post('/', _incrementHandler) ..get('/', _getValueHandler);
..get('/', _getValueHandler);
Future<Response> _incrementHandler(Request request) async { Future<Response> _incrementHandler(Request request) async {
final incr = Increment.fromJson(json.decode(await request.readAsString())); final incr = Increment.fromJson(json.decode(await request.readAsString()));

@ -2,9 +2,10 @@ name: server
description: A server app using the shelf package and Docker. description: A server app using the shelf package and Docker.
version: 1.0.0 version: 1.0.0
publish_to: "none" publish_to: "none"
resolution: workspace
environment: environment:
sdk: ^3.7.0-0 sdk: ^3.9.0-0
dependencies: dependencies:
args: ^2.0.0 args: ^2.0.0
@ -14,6 +15,8 @@ dependencies:
path: ../shared path: ../shared
dev_dependencies: dev_dependencies:
analysis_defaults:
path: ../../analysis_defaults
http: ^1.0.0 http: ^1.0.0
lints: ^6.0.0 lints: ^6.0.0
test: ^1.15.0 test: ^1.15.0

@ -52,11 +52,10 @@ class _$IncrementCopyWithImpl<$Res, $Val extends Increment>
$Res call({Object? by = null}) { $Res call({Object? by = null}) {
return _then( return _then(
_value.copyWith( _value.copyWith(
by: by: null == by
null == by ? _value.by
? _value.by : by // ignore: cast_nullable_to_non_nullable
: by // ignore: cast_nullable_to_non_nullable as int,
as int,
) )
as $Val, as $Val,
); );
@ -89,11 +88,10 @@ class __$$IncrementImplCopyWithImpl<$Res>
$Res call({Object? by = null}) { $Res call({Object? by = null}) {
return _then( return _then(
_$IncrementImpl( _$IncrementImpl(
by: by: null == by
null == by ? _value.by
? _value.by : by // ignore: cast_nullable_to_non_nullable
: by // ignore: cast_nullable_to_non_nullable as int,
as int,
), ),
); );
} }
@ -189,11 +187,10 @@ class _$CountCopyWithImpl<$Res, $Val extends Count>
$Res call({Object? value = null}) { $Res call({Object? value = null}) {
return _then( return _then(
_value.copyWith( _value.copyWith(
value: value: null == value
null == value ? _value.value
? _value.value : value // ignore: cast_nullable_to_non_nullable
: value // ignore: cast_nullable_to_non_nullable as int,
as int,
) )
as $Val, as $Val,
); );
@ -228,7 +225,7 @@ class __$$CountImplCopyWithImpl<$Res>
null == value null == value
? _value.value ? _value.value
: value // ignore: cast_nullable_to_non_nullable : value // ignore: cast_nullable_to_non_nullable
as int, as int,
), ),
); );
} }

@ -1,15 +1,18 @@
name: shared name: shared
description: Common data models required by our client and server description: Common data models required by our client and server
version: 1.0.0 version: 1.0.0
resolution: workspace
environment: environment:
sdk: ^3.7.0-0 sdk: ^3.9.0-0
dependencies: dependencies:
freezed_annotation: ">=2.1.0 <4.0.0" freezed_annotation: ">=2.1.0 <4.0.0"
json_annotation: ^4.7.0 json_annotation: ^4.7.0
dev_dependencies: dev_dependencies:
analysis_defaults:
path: ../../analysis_defaults
build_runner: ^2.2.1 build_runner: ^2.2.1
freezed: ">=2.1.1 <4.0.0" freezed: ">=2.1.1 <4.0.0"
json_serializable: ^6.4.0 json_serializable: ^6.4.0

@ -36,12 +36,11 @@ import '../domain/use_cases/booking/booking_share_use_case.dart';
List<SingleChildWidget> _sharedProviders = [ List<SingleChildWidget> _sharedProviders = [
Provider( Provider(
lazy: true, lazy: true,
create: create: (context) => BookingCreateUseCase(
(context) => BookingCreateUseCase( destinationRepository: context.read(),
destinationRepository: context.read(), activityRepository: context.read(),
activityRepository: context.read(), bookingRepository: context.read(),
bookingRepository: context.read(), ),
),
), ),
Provider( Provider(
lazy: true, lazy: true,
@ -57,46 +56,40 @@ List<SingleChildWidget> get providersRemote {
Provider(create: (context) => ApiClient()), Provider(create: (context) => ApiClient()),
Provider(create: (context) => SharedPreferencesService()), Provider(create: (context) => SharedPreferencesService()),
ChangeNotifierProvider( ChangeNotifierProvider(
create: create: (context) =>
(context) => AuthRepositoryRemote(
AuthRepositoryRemote( authApiClient: context.read(),
authApiClient: context.read(), apiClient: context.read(),
apiClient: context.read(), sharedPreferencesService: context.read(),
sharedPreferencesService: context.read(), )
) as AuthRepository,
as AuthRepository,
), ),
Provider( Provider(
create: create: (context) =>
(context) => DestinationRepositoryRemote(apiClient: context.read())
DestinationRepositoryRemote(apiClient: context.read()) as DestinationRepository,
as DestinationRepository,
), ),
Provider( Provider(
create: create: (context) =>
(context) => ContinentRepositoryRemote(apiClient: context.read())
ContinentRepositoryRemote(apiClient: context.read()) as ContinentRepository,
as ContinentRepository,
), ),
Provider( Provider(
create: create: (context) =>
(context) => ActivityRepositoryRemote(apiClient: context.read())
ActivityRepositoryRemote(apiClient: context.read()) as ActivityRepository,
as ActivityRepository,
), ),
Provider.value( Provider.value(
value: ItineraryConfigRepositoryMemory() as ItineraryConfigRepository, value: ItineraryConfigRepositoryMemory() as ItineraryConfigRepository,
), ),
Provider( Provider(
create: create: (context) =>
(context) => BookingRepositoryRemote(apiClient: context.read())
BookingRepositoryRemote(apiClient: context.read()) as BookingRepository,
as BookingRepository,
), ),
Provider( Provider(
create: create: (context) =>
(context) => UserRepositoryRemote(apiClient: context.read()) as UserRepository,
UserRepositoryRemote(apiClient: context.read()) as UserRepository,
), ),
..._sharedProviders, ..._sharedProviders,
]; ];
@ -110,37 +103,32 @@ List<SingleChildWidget> get providersLocal {
ChangeNotifierProvider.value(value: AuthRepositoryDev() as AuthRepository), ChangeNotifierProvider.value(value: AuthRepositoryDev() as AuthRepository),
Provider.value(value: LocalDataService()), Provider.value(value: LocalDataService()),
Provider( Provider(
create: create: (context) =>
(context) => DestinationRepositoryLocal(localDataService: context.read())
DestinationRepositoryLocal(localDataService: context.read()) as DestinationRepository,
as DestinationRepository,
), ),
Provider( Provider(
create: create: (context) =>
(context) => ContinentRepositoryLocal(localDataService: context.read())
ContinentRepositoryLocal(localDataService: context.read()) as ContinentRepository,
as ContinentRepository,
), ),
Provider( Provider(
create: create: (context) =>
(context) => ActivityRepositoryLocal(localDataService: context.read())
ActivityRepositoryLocal(localDataService: context.read()) as ActivityRepository,
as ActivityRepository,
), ),
Provider( Provider(
create: create: (context) =>
(context) => BookingRepositoryLocal(localDataService: context.read())
BookingRepositoryLocal(localDataService: context.read()) as BookingRepository,
as BookingRepository,
), ),
Provider.value( Provider.value(
value: ItineraryConfigRepositoryMemory() as ItineraryConfigRepository, value: ItineraryConfigRepositoryMemory() as ItineraryConfigRepository,
), ),
Provider( Provider(
create: create: (context) =>
(context) => UserRepositoryLocal(localDataService: context.read())
UserRepositoryLocal(localDataService: context.read()) as UserRepository,
as UserRepository,
), ),
..._sharedProviders, ..._sharedProviders,
]; ];

@ -18,10 +18,9 @@ class ActivityRepositoryLocal implements ActivityRepository {
@override @override
Future<Result<List<Activity>>> getByDestination(String ref) async { Future<Result<List<Activity>>> getByDestination(String ref) async {
try { try {
final activities = final activities = (await _localDataService.getActivities())
(await _localDataService.getActivities()) .where((activity) => activity.destinationRef == ref)
.where((activity) => activity.destinationRef == ref) .toList();
.toList();
return Result.ok(activities); return Result.ok(activities);
} on Exception catch (error) { } on Exception catch (error) {

@ -68,11 +68,10 @@ class BookingRepositoryLocal implements BookingRepository {
// create a default booking the first time // create a default booking the first time
if (_bookings.isEmpty) { if (_bookings.isEmpty) {
final destination = (await _localDataService.getDestinations()).first; final destination = (await _localDataService.getDestinations()).first;
final activities = final activities = (await _localDataService.getActivities())
(await _localDataService.getActivities()) .where((activity) => activity.destinationRef == destination.ref)
.where((activity) => activity.destinationRef == destination.ref) .take(4)
.take(4) .toList();
.toList();
_bookings.add( _bookings.add(
Booking( Booking(

@ -27,8 +27,9 @@ class BookingRepositoryRemote implements BookingRepository {
endDate: booking.endDate, endDate: booking.endDate,
name: '${booking.destination.name}, ${booking.destination.continent}', name: '${booking.destination.name}, ${booking.destination.continent}',
destinationRef: booking.destination.ref, destinationRef: booking.destination.ref,
activitiesRef: activitiesRef: booking.activity
booking.activity.map((activity) => activity.ref).toList(), .map((activity) => activity.ref)
.toList(),
); );
return _apiClient.postBooking(bookingApiModel); return _apiClient.postBooking(bookingApiModel);
} on Exception catch (e) { } on Exception catch (e) {
@ -72,10 +73,9 @@ class BookingRepositoryRemote implements BookingRepository {
return Result.error(resultActivities.error); return Result.error(resultActivities.error);
case Ok<List<Activity>>(): case Ok<List<Activity>>():
} }
final activities = final activities = resultActivities.value
resultActivities.value .where((activity) => booking.activitiesRef.contains(activity.ref))
.where((activity) => booking.activitiesRef.contains(activity.ref)) .toList();
.toList();
return Result.ok( return Result.ok(
Booking( Booking(

@ -95,8 +95,9 @@ class ApiClient {
if (response.statusCode == 200) { if (response.statusCode == 200) {
final stringData = await response.transform(utf8.decoder).join(); final stringData = await response.transform(utf8.decoder).join();
final json = jsonDecode(stringData) as List<dynamic>; final json = jsonDecode(stringData) as List<dynamic>;
final activities = final activities = json
json.map((element) => Activity.fromJson(element)).toList(); .map((element) => Activity.fromJson(element))
.toList();
return Result.ok(activities); return Result.ok(activities);
} else { } else {
return const Result.error(HttpException("Invalid response")); return const Result.error(HttpException("Invalid response"));
@ -117,8 +118,9 @@ class ApiClient {
if (response.statusCode == 200) { if (response.statusCode == 200) {
final stringData = await response.transform(utf8.decoder).join(); final stringData = await response.transform(utf8.decoder).join();
final json = jsonDecode(stringData) as List<dynamic>; final json = jsonDecode(stringData) as List<dynamic>;
final bookings = final bookings = json
json.map((element) => BookingApiModel.fromJson(element)).toList(); .map((element) => BookingApiModel.fromJson(element))
.toList();
return Result.ok(bookings); return Result.ok(bookings);
} else { } else {
return const Result.error(HttpException("Invalid response")); return const Result.error(HttpException("Invalid response"));

@ -91,36 +91,30 @@ class _$BookingApiModelCopyWithImpl<$Res, $Val extends BookingApiModel>
}) { }) {
return _then( return _then(
_value.copyWith( _value.copyWith(
id: id: freezed == id
freezed == id ? _value.id
? _value.id : id // ignore: cast_nullable_to_non_nullable
: id // ignore: cast_nullable_to_non_nullable as int?,
as int?, startDate: null == startDate
startDate: ? _value.startDate
null == startDate : startDate // ignore: cast_nullable_to_non_nullable
? _value.startDate as DateTime,
: startDate // ignore: cast_nullable_to_non_nullable endDate: null == endDate
as DateTime, ? _value.endDate
endDate: : endDate // ignore: cast_nullable_to_non_nullable
null == endDate as DateTime,
? _value.endDate name: null == name
: endDate // ignore: cast_nullable_to_non_nullable ? _value.name
as DateTime, : name // ignore: cast_nullable_to_non_nullable
name: as String,
null == name destinationRef: null == destinationRef
? _value.name ? _value.destinationRef
: name // ignore: cast_nullable_to_non_nullable : destinationRef // ignore: cast_nullable_to_non_nullable
as String, as String,
destinationRef: activitiesRef: null == activitiesRef
null == destinationRef ? _value.activitiesRef
? _value.destinationRef : activitiesRef // ignore: cast_nullable_to_non_nullable
: destinationRef // ignore: cast_nullable_to_non_nullable as List<String>,
as String,
activitiesRef:
null == activitiesRef
? _value.activitiesRef
: activitiesRef // ignore: cast_nullable_to_non_nullable
as List<String>,
) )
as $Val, as $Val,
); );
@ -169,36 +163,30 @@ class __$$BookingApiModelImplCopyWithImpl<$Res>
}) { }) {
return _then( return _then(
_$BookingApiModelImpl( _$BookingApiModelImpl(
id: id: freezed == id
freezed == id ? _value.id
? _value.id : id // ignore: cast_nullable_to_non_nullable
: id // ignore: cast_nullable_to_non_nullable as int?,
as int?, startDate: null == startDate
startDate: ? _value.startDate
null == startDate : startDate // ignore: cast_nullable_to_non_nullable
? _value.startDate as DateTime,
: startDate // ignore: cast_nullable_to_non_nullable endDate: null == endDate
as DateTime, ? _value.endDate
endDate: : endDate // ignore: cast_nullable_to_non_nullable
null == endDate as DateTime,
? _value.endDate name: null == name
: endDate // ignore: cast_nullable_to_non_nullable ? _value.name
as DateTime, : name // ignore: cast_nullable_to_non_nullable
name: as String,
null == name destinationRef: null == destinationRef
? _value.name ? _value.destinationRef
: name // ignore: cast_nullable_to_non_nullable : destinationRef // ignore: cast_nullable_to_non_nullable
as String, as String,
destinationRef: activitiesRef: null == activitiesRef
null == destinationRef ? _value._activitiesRef
? _value.destinationRef : activitiesRef // ignore: cast_nullable_to_non_nullable
: destinationRef // ignore: cast_nullable_to_non_nullable as List<String>,
as String,
activitiesRef:
null == activitiesRef
? _value._activitiesRef
: activitiesRef // ignore: cast_nullable_to_non_nullable
as List<String>,
), ),
); );
} }

@ -14,8 +14,9 @@ _$BookingApiModelImpl _$$BookingApiModelImplFromJson(
endDate: DateTime.parse(json['endDate'] as String), endDate: DateTime.parse(json['endDate'] as String),
name: json['name'] as String, name: json['name'] as String,
destinationRef: json['destinationRef'] as String, destinationRef: json['destinationRef'] as String,
activitiesRef: activitiesRef: (json['activitiesRef'] as List<dynamic>)
(json['activitiesRef'] as List<dynamic>).map((e) => e as String).toList(), .map((e) => e as String)
.toList(),
); );
Map<String, dynamic> _$$BookingApiModelImplToJson( Map<String, dynamic> _$$BookingApiModelImplToJson(

@ -64,16 +64,14 @@ class _$LoginRequestCopyWithImpl<$Res, $Val extends LoginRequest>
$Res call({Object? email = null, Object? password = null}) { $Res call({Object? email = null, Object? password = null}) {
return _then( return _then(
_value.copyWith( _value.copyWith(
email: email: null == email
null == email ? _value.email
? _value.email : email // ignore: cast_nullable_to_non_nullable
: email // ignore: cast_nullable_to_non_nullable as String,
as String, password: null == password
password: ? _value.password
null == password : password // ignore: cast_nullable_to_non_nullable
? _value.password as String,
: password // ignore: cast_nullable_to_non_nullable
as String,
) )
as $Val, as $Val,
); );
@ -108,16 +106,14 @@ class __$$LoginRequestImplCopyWithImpl<$Res>
$Res call({Object? email = null, Object? password = null}) { $Res call({Object? email = null, Object? password = null}) {
return _then( return _then(
_$LoginRequestImpl( _$LoginRequestImpl(
email: email: null == email
null == email ? _value.email
? _value.email : email // ignore: cast_nullable_to_non_nullable
: email // ignore: cast_nullable_to_non_nullable as String,
as String, password: null == password
password: ? _value.password
null == password : password // ignore: cast_nullable_to_non_nullable
? _value.password as String,
: password // ignore: cast_nullable_to_non_nullable
as String,
), ),
); );
} }

@ -64,16 +64,14 @@ class _$LoginResponseCopyWithImpl<$Res, $Val extends LoginResponse>
$Res call({Object? token = null, Object? userId = null}) { $Res call({Object? token = null, Object? userId = null}) {
return _then( return _then(
_value.copyWith( _value.copyWith(
token: token: null == token
null == token ? _value.token
? _value.token : token // ignore: cast_nullable_to_non_nullable
: token // ignore: cast_nullable_to_non_nullable as String,
as String, userId: null == userId
userId: ? _value.userId
null == userId : userId // ignore: cast_nullable_to_non_nullable
? _value.userId as String,
: userId // ignore: cast_nullable_to_non_nullable
as String,
) )
as $Val, as $Val,
); );
@ -108,16 +106,14 @@ class __$$LoginResponseImplCopyWithImpl<$Res>
$Res call({Object? token = null, Object? userId = null}) { $Res call({Object? token = null, Object? userId = null}) {
return _then( return _then(
_$LoginResponseImpl( _$LoginResponseImpl(
token: token: null == token
null == token ? _value.token
? _value.token : token // ignore: cast_nullable_to_non_nullable
: token // ignore: cast_nullable_to_non_nullable as String,
as String, userId: null == userId
userId: ? _value.userId
null == userId : userId // ignore: cast_nullable_to_non_nullable
? _value.userId as String,
: userId // ignore: cast_nullable_to_non_nullable
as String,
), ),
); );
} }

@ -75,26 +75,22 @@ class _$UserApiModelCopyWithImpl<$Res, $Val extends UserApiModel>
}) { }) {
return _then( return _then(
_value.copyWith( _value.copyWith(
id: id: null == id
null == id ? _value.id
? _value.id : id // ignore: cast_nullable_to_non_nullable
: id // ignore: cast_nullable_to_non_nullable as String,
as String, name: null == name
name: ? _value.name
null == name : name // ignore: cast_nullable_to_non_nullable
? _value.name as String,
: name // ignore: cast_nullable_to_non_nullable email: null == email
as String, ? _value.email
email: : email // ignore: cast_nullable_to_non_nullable
null == email as String,
? _value.email picture: null == picture
: email // ignore: cast_nullable_to_non_nullable ? _value.picture
as String, : picture // ignore: cast_nullable_to_non_nullable
picture: as String,
null == picture
? _value.picture
: picture // ignore: cast_nullable_to_non_nullable
as String,
) )
as $Val, as $Val,
); );
@ -134,26 +130,22 @@ class __$$UserApiModelImplCopyWithImpl<$Res>
}) { }) {
return _then( return _then(
_$UserApiModelImpl( _$UserApiModelImpl(
id: id: null == id
null == id ? _value.id
? _value.id : id // ignore: cast_nullable_to_non_nullable
: id // ignore: cast_nullable_to_non_nullable as String,
as String, name: null == name
name: ? _value.name
null == name : name // ignore: cast_nullable_to_non_nullable
? _value.name as String,
: name // ignore: cast_nullable_to_non_nullable email: null == email
as String, ? _value.email
email: : email // ignore: cast_nullable_to_non_nullable
null == email as String,
? _value.email picture: null == picture
: email // ignore: cast_nullable_to_non_nullable ? _value.picture
as String, : picture // ignore: cast_nullable_to_non_nullable
picture: as String,
null == picture
? _value.picture
: picture // ignore: cast_nullable_to_non_nullable
as String,
), ),
); );
} }

@ -109,56 +109,46 @@ class _$ActivityCopyWithImpl<$Res, $Val extends Activity>
}) { }) {
return _then( return _then(
_value.copyWith( _value.copyWith(
name: name: null == name
null == name ? _value.name
? _value.name : name // ignore: cast_nullable_to_non_nullable
: name // ignore: cast_nullable_to_non_nullable as String,
as String, description: null == description
description: ? _value.description
null == description : description // ignore: cast_nullable_to_non_nullable
? _value.description as String,
: description // ignore: cast_nullable_to_non_nullable locationName: null == locationName
as String, ? _value.locationName
locationName: : locationName // ignore: cast_nullable_to_non_nullable
null == locationName as String,
? _value.locationName duration: null == duration
: locationName // ignore: cast_nullable_to_non_nullable ? _value.duration
as String, : duration // ignore: cast_nullable_to_non_nullable
duration: as int,
null == duration timeOfDay: null == timeOfDay
? _value.duration ? _value.timeOfDay
: duration // ignore: cast_nullable_to_non_nullable : timeOfDay // ignore: cast_nullable_to_non_nullable
as int, as TimeOfDay,
timeOfDay: familyFriendly: null == familyFriendly
null == timeOfDay ? _value.familyFriendly
? _value.timeOfDay : familyFriendly // ignore: cast_nullable_to_non_nullable
: timeOfDay // ignore: cast_nullable_to_non_nullable as bool,
as TimeOfDay, price: null == price
familyFriendly: ? _value.price
null == familyFriendly : price // ignore: cast_nullable_to_non_nullable
? _value.familyFriendly as int,
: familyFriendly // ignore: cast_nullable_to_non_nullable destinationRef: null == destinationRef
as bool, ? _value.destinationRef
price: : destinationRef // ignore: cast_nullable_to_non_nullable
null == price as String,
? _value.price ref: null == ref
: price // ignore: cast_nullable_to_non_nullable ? _value.ref
as int, : ref // ignore: cast_nullable_to_non_nullable
destinationRef: as String,
null == destinationRef imageUrl: null == imageUrl
? _value.destinationRef ? _value.imageUrl
: destinationRef // ignore: cast_nullable_to_non_nullable : imageUrl // ignore: cast_nullable_to_non_nullable
as String, as String,
ref:
null == ref
? _value.ref
: ref // ignore: cast_nullable_to_non_nullable
as String,
imageUrl:
null == imageUrl
? _value.imageUrl
: imageUrl // ignore: cast_nullable_to_non_nullable
as String,
) )
as $Val, as $Val,
); );
@ -215,56 +205,46 @@ class __$$ActivityImplCopyWithImpl<$Res>
}) { }) {
return _then( return _then(
_$ActivityImpl( _$ActivityImpl(
name: name: null == name
null == name ? _value.name
? _value.name : name // ignore: cast_nullable_to_non_nullable
: name // ignore: cast_nullable_to_non_nullable as String,
as String, description: null == description
description: ? _value.description
null == description : description // ignore: cast_nullable_to_non_nullable
? _value.description as String,
: description // ignore: cast_nullable_to_non_nullable locationName: null == locationName
as String, ? _value.locationName
locationName: : locationName // ignore: cast_nullable_to_non_nullable
null == locationName as String,
? _value.locationName duration: null == duration
: locationName // ignore: cast_nullable_to_non_nullable ? _value.duration
as String, : duration // ignore: cast_nullable_to_non_nullable
duration: as int,
null == duration timeOfDay: null == timeOfDay
? _value.duration ? _value.timeOfDay
: duration // ignore: cast_nullable_to_non_nullable : timeOfDay // ignore: cast_nullable_to_non_nullable
as int, as TimeOfDay,
timeOfDay: familyFriendly: null == familyFriendly
null == timeOfDay ? _value.familyFriendly
? _value.timeOfDay : familyFriendly // ignore: cast_nullable_to_non_nullable
: timeOfDay // ignore: cast_nullable_to_non_nullable as bool,
as TimeOfDay, price: null == price
familyFriendly: ? _value.price
null == familyFriendly : price // ignore: cast_nullable_to_non_nullable
? _value.familyFriendly as int,
: familyFriendly // ignore: cast_nullable_to_non_nullable destinationRef: null == destinationRef
as bool, ? _value.destinationRef
price: : destinationRef // ignore: cast_nullable_to_non_nullable
null == price as String,
? _value.price ref: null == ref
: price // ignore: cast_nullable_to_non_nullable ? _value.ref
as int, : ref // ignore: cast_nullable_to_non_nullable
destinationRef: as String,
null == destinationRef imageUrl: null == imageUrl
? _value.destinationRef ? _value.imageUrl
: destinationRef // ignore: cast_nullable_to_non_nullable : imageUrl // ignore: cast_nullable_to_non_nullable
as String, as String,
ref:
null == ref
? _value.ref
: ref // ignore: cast_nullable_to_non_nullable
as String,
imageUrl:
null == imageUrl
? _value.imageUrl
: imageUrl // ignore: cast_nullable_to_non_nullable
as String,
), ),
); );
} }

@ -85,31 +85,26 @@ class _$BookingCopyWithImpl<$Res, $Val extends Booking>
}) { }) {
return _then( return _then(
_value.copyWith( _value.copyWith(
id: id: freezed == id
freezed == id ? _value.id
? _value.id : id // ignore: cast_nullable_to_non_nullable
: id // ignore: cast_nullable_to_non_nullable as int?,
as int?, startDate: null == startDate
startDate: ? _value.startDate
null == startDate : startDate // ignore: cast_nullable_to_non_nullable
? _value.startDate as DateTime,
: startDate // ignore: cast_nullable_to_non_nullable endDate: null == endDate
as DateTime, ? _value.endDate
endDate: : endDate // ignore: cast_nullable_to_non_nullable
null == endDate as DateTime,
? _value.endDate destination: null == destination
: endDate // ignore: cast_nullable_to_non_nullable ? _value.destination
as DateTime, : destination // ignore: cast_nullable_to_non_nullable
destination: as Destination,
null == destination activity: null == activity
? _value.destination ? _value.activity
: destination // ignore: cast_nullable_to_non_nullable : activity // ignore: cast_nullable_to_non_nullable
as Destination, as List<Activity>,
activity:
null == activity
? _value.activity
: activity // ignore: cast_nullable_to_non_nullable
as List<Activity>,
) )
as $Val, as $Val,
); );
@ -168,31 +163,26 @@ class __$$BookingImplCopyWithImpl<$Res>
}) { }) {
return _then( return _then(
_$BookingImpl( _$BookingImpl(
id: id: freezed == id
freezed == id ? _value.id
? _value.id : id // ignore: cast_nullable_to_non_nullable
: id // ignore: cast_nullable_to_non_nullable as int?,
as int?, startDate: null == startDate
startDate: ? _value.startDate
null == startDate : startDate // ignore: cast_nullable_to_non_nullable
? _value.startDate as DateTime,
: startDate // ignore: cast_nullable_to_non_nullable endDate: null == endDate
as DateTime, ? _value.endDate
endDate: : endDate // ignore: cast_nullable_to_non_nullable
null == endDate as DateTime,
? _value.endDate destination: null == destination
: endDate // ignore: cast_nullable_to_non_nullable ? _value.destination
as DateTime, : destination // ignore: cast_nullable_to_non_nullable
destination: as Destination,
null == destination activity: null == activity
? _value.destination ? _value._activity
: destination // ignore: cast_nullable_to_non_nullable : activity // ignore: cast_nullable_to_non_nullable
as Destination, as List<Activity>,
activity:
null == activity
? _value._activity
: activity // ignore: cast_nullable_to_non_nullable
as List<Activity>,
), ),
); );
} }

@ -14,10 +14,9 @@ _$BookingImpl _$$BookingImplFromJson(Map<String, dynamic> json) =>
destination: Destination.fromJson( destination: Destination.fromJson(
json['destination'] as Map<String, dynamic>, json['destination'] as Map<String, dynamic>,
), ),
activity: activity: (json['activity'] as List<dynamic>)
(json['activity'] as List<dynamic>) .map((e) => Activity.fromJson(e as Map<String, dynamic>))
.map((e) => Activity.fromJson(e as Map<String, dynamic>)) .toList(),
.toList(),
); );
Map<String, dynamic> _$$BookingImplToJson(_$BookingImpl instance) => Map<String, dynamic> _$$BookingImplToJson(_$BookingImpl instance) =>

@ -75,26 +75,22 @@ class _$BookingSummaryCopyWithImpl<$Res, $Val extends BookingSummary>
}) { }) {
return _then( return _then(
_value.copyWith( _value.copyWith(
id: id: null == id
null == id ? _value.id
? _value.id : id // ignore: cast_nullable_to_non_nullable
: id // ignore: cast_nullable_to_non_nullable as int,
as int, name: null == name
name: ? _value.name
null == name : name // ignore: cast_nullable_to_non_nullable
? _value.name as String,
: name // ignore: cast_nullable_to_non_nullable startDate: null == startDate
as String, ? _value.startDate
startDate: : startDate // ignore: cast_nullable_to_non_nullable
null == startDate as DateTime,
? _value.startDate endDate: null == endDate
: startDate // ignore: cast_nullable_to_non_nullable ? _value.endDate
as DateTime, : endDate // ignore: cast_nullable_to_non_nullable
endDate: as DateTime,
null == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime,
) )
as $Val, as $Val,
); );
@ -134,26 +130,22 @@ class __$$BookingSummaryImplCopyWithImpl<$Res>
}) { }) {
return _then( return _then(
_$BookingSummaryImpl( _$BookingSummaryImpl(
id: id: null == id
null == id ? _value.id
? _value.id : id // ignore: cast_nullable_to_non_nullable
: id // ignore: cast_nullable_to_non_nullable as int,
as int, name: null == name
name: ? _value.name
null == name : name // ignore: cast_nullable_to_non_nullable
? _value.name as String,
: name // ignore: cast_nullable_to_non_nullable startDate: null == startDate
as String, ? _value.startDate
startDate: : startDate // ignore: cast_nullable_to_non_nullable
null == startDate as DateTime,
? _value.startDate endDate: null == endDate
: startDate // ignore: cast_nullable_to_non_nullable ? _value.endDate
as DateTime, : endDate // ignore: cast_nullable_to_non_nullable
endDate: as DateTime,
null == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime,
), ),
); );
} }

@ -62,16 +62,14 @@ class _$ContinentCopyWithImpl<$Res, $Val extends Continent>
$Res call({Object? name = null, Object? imageUrl = null}) { $Res call({Object? name = null, Object? imageUrl = null}) {
return _then( return _then(
_value.copyWith( _value.copyWith(
name: name: null == name
null == name ? _value.name
? _value.name : name // ignore: cast_nullable_to_non_nullable
: name // ignore: cast_nullable_to_non_nullable as String,
as String, imageUrl: null == imageUrl
imageUrl: ? _value.imageUrl
null == imageUrl : imageUrl // ignore: cast_nullable_to_non_nullable
? _value.imageUrl as String,
: imageUrl // ignore: cast_nullable_to_non_nullable
as String,
) )
as $Val, as $Val,
); );
@ -106,16 +104,14 @@ class __$$ContinentImplCopyWithImpl<$Res>
$Res call({Object? name = null, Object? imageUrl = null}) { $Res call({Object? name = null, Object? imageUrl = null}) {
return _then( return _then(
_$ContinentImpl( _$ContinentImpl(
name: name: null == name
null == name ? _value.name
? _value.name : name // ignore: cast_nullable_to_non_nullable
: name // ignore: cast_nullable_to_non_nullable as String,
as String, imageUrl: null == imageUrl
imageUrl: ? _value.imageUrl
null == imageUrl : imageUrl // ignore: cast_nullable_to_non_nullable
? _value.imageUrl as String,
: imageUrl // ignore: cast_nullable_to_non_nullable
as String,
), ),
); );
} }

@ -95,41 +95,34 @@ class _$DestinationCopyWithImpl<$Res, $Val extends Destination>
}) { }) {
return _then( return _then(
_value.copyWith( _value.copyWith(
ref: ref: null == ref
null == ref ? _value.ref
? _value.ref : ref // ignore: cast_nullable_to_non_nullable
: ref // ignore: cast_nullable_to_non_nullable as String,
as String, name: null == name
name: ? _value.name
null == name : name // ignore: cast_nullable_to_non_nullable
? _value.name as String,
: name // ignore: cast_nullable_to_non_nullable country: null == country
as String, ? _value.country
country: : country // ignore: cast_nullable_to_non_nullable
null == country as String,
? _value.country continent: null == continent
: country // ignore: cast_nullable_to_non_nullable ? _value.continent
as String, : continent // ignore: cast_nullable_to_non_nullable
continent: as String,
null == continent knownFor: null == knownFor
? _value.continent ? _value.knownFor
: continent // ignore: cast_nullable_to_non_nullable : knownFor // ignore: cast_nullable_to_non_nullable
as String, as String,
knownFor: tags: null == tags
null == knownFor ? _value.tags
? _value.knownFor : tags // ignore: cast_nullable_to_non_nullable
: knownFor // ignore: cast_nullable_to_non_nullable as List<String>,
as String, imageUrl: null == imageUrl
tags: ? _value.imageUrl
null == tags : imageUrl // ignore: cast_nullable_to_non_nullable
? _value.tags as String,
: tags // ignore: cast_nullable_to_non_nullable
as List<String>,
imageUrl:
null == imageUrl
? _value.imageUrl
: imageUrl // ignore: cast_nullable_to_non_nullable
as String,
) )
as $Val, as $Val,
); );
@ -180,41 +173,34 @@ class __$$DestinationImplCopyWithImpl<$Res>
}) { }) {
return _then( return _then(
_$DestinationImpl( _$DestinationImpl(
ref: ref: null == ref
null == ref ? _value.ref
? _value.ref : ref // ignore: cast_nullable_to_non_nullable
: ref // ignore: cast_nullable_to_non_nullable as String,
as String, name: null == name
name: ? _value.name
null == name : name // ignore: cast_nullable_to_non_nullable
? _value.name as String,
: name // ignore: cast_nullable_to_non_nullable country: null == country
as String, ? _value.country
country: : country // ignore: cast_nullable_to_non_nullable
null == country as String,
? _value.country continent: null == continent
: country // ignore: cast_nullable_to_non_nullable ? _value.continent
as String, : continent // ignore: cast_nullable_to_non_nullable
continent: as String,
null == continent knownFor: null == knownFor
? _value.continent ? _value.knownFor
: continent // ignore: cast_nullable_to_non_nullable : knownFor // ignore: cast_nullable_to_non_nullable
as String, as String,
knownFor: tags: null == tags
null == knownFor ? _value._tags
? _value.knownFor : tags // ignore: cast_nullable_to_non_nullable
: knownFor // ignore: cast_nullable_to_non_nullable as List<String>,
as String, imageUrl: null == imageUrl
tags: ? _value.imageUrl
null == tags : imageUrl // ignore: cast_nullable_to_non_nullable
? _value._tags as String,
: tags // ignore: cast_nullable_to_non_nullable
as List<String>,
imageUrl:
null == imageUrl
? _value.imageUrl
: imageUrl // ignore: cast_nullable_to_non_nullable
as String,
), ),
); );
} }

@ -90,36 +90,30 @@ class _$ItineraryConfigCopyWithImpl<$Res, $Val extends ItineraryConfig>
}) { }) {
return _then( return _then(
_value.copyWith( _value.copyWith(
continent: continent: freezed == continent
freezed == continent ? _value.continent
? _value.continent : continent // ignore: cast_nullable_to_non_nullable
: continent // ignore: cast_nullable_to_non_nullable as String?,
as String?, startDate: freezed == startDate
startDate: ? _value.startDate
freezed == startDate : startDate // ignore: cast_nullable_to_non_nullable
? _value.startDate as DateTime?,
: startDate // ignore: cast_nullable_to_non_nullable endDate: freezed == endDate
as DateTime?, ? _value.endDate
endDate: : endDate // ignore: cast_nullable_to_non_nullable
freezed == endDate as DateTime?,
? _value.endDate guests: freezed == guests
: endDate // ignore: cast_nullable_to_non_nullable ? _value.guests
as DateTime?, : guests // ignore: cast_nullable_to_non_nullable
guests: as int?,
freezed == guests destination: freezed == destination
? _value.guests ? _value.destination
: guests // ignore: cast_nullable_to_non_nullable : destination // ignore: cast_nullable_to_non_nullable
as int?, as String?,
destination: activities: null == activities
freezed == destination ? _value.activities
? _value.destination : activities // ignore: cast_nullable_to_non_nullable
: destination // ignore: cast_nullable_to_non_nullable as List<String>,
as String?,
activities:
null == activities
? _value.activities
: activities // ignore: cast_nullable_to_non_nullable
as List<String>,
) )
as $Val, as $Val,
); );
@ -168,36 +162,30 @@ class __$$ItineraryConfigImplCopyWithImpl<$Res>
}) { }) {
return _then( return _then(
_$ItineraryConfigImpl( _$ItineraryConfigImpl(
continent: continent: freezed == continent
freezed == continent ? _value.continent
? _value.continent : continent // ignore: cast_nullable_to_non_nullable
: continent // ignore: cast_nullable_to_non_nullable as String?,
as String?, startDate: freezed == startDate
startDate: ? _value.startDate
freezed == startDate : startDate // ignore: cast_nullable_to_non_nullable
? _value.startDate as DateTime?,
: startDate // ignore: cast_nullable_to_non_nullable endDate: freezed == endDate
as DateTime?, ? _value.endDate
endDate: : endDate // ignore: cast_nullable_to_non_nullable
freezed == endDate as DateTime?,
? _value.endDate guests: freezed == guests
: endDate // ignore: cast_nullable_to_non_nullable ? _value.guests
as DateTime?, : guests // ignore: cast_nullable_to_non_nullable
guests: as int?,
freezed == guests destination: freezed == destination
? _value.guests ? _value.destination
: guests // ignore: cast_nullable_to_non_nullable : destination // ignore: cast_nullable_to_non_nullable
as int?, as String?,
destination: activities: null == activities
freezed == destination ? _value._activities
? _value.destination : activities // ignore: cast_nullable_to_non_nullable
: destination // ignore: cast_nullable_to_non_nullable as List<String>,
as String?,
activities:
null == activities
? _value._activities
: activities // ignore: cast_nullable_to_non_nullable
as List<String>,
), ),
); );
} }

@ -10,14 +10,12 @@ _$ItineraryConfigImpl _$$ItineraryConfigImplFromJson(
Map<String, dynamic> json, Map<String, dynamic> json,
) => _$ItineraryConfigImpl( ) => _$ItineraryConfigImpl(
continent: json['continent'] as String?, continent: json['continent'] as String?,
startDate: startDate: json['startDate'] == null
json['startDate'] == null ? null
? null : DateTime.parse(json['startDate'] as String),
: DateTime.parse(json['startDate'] as String), endDate: json['endDate'] == null
endDate: ? null
json['endDate'] == null : DateTime.parse(json['endDate'] as String),
? null
: DateTime.parse(json['endDate'] as String),
guests: (json['guests'] as num?)?.toInt(), guests: (json['guests'] as num?)?.toInt(),
destination: json['destination'] as String?, destination: json['destination'] as String?,
activities: activities:

@ -61,16 +61,14 @@ class _$UserCopyWithImpl<$Res, $Val extends User>
$Res call({Object? name = null, Object? picture = null}) { $Res call({Object? name = null, Object? picture = null}) {
return _then( return _then(
_value.copyWith( _value.copyWith(
name: name: null == name
null == name ? _value.name
? _value.name : name // ignore: cast_nullable_to_non_nullable
: name // ignore: cast_nullable_to_non_nullable as String,
as String, picture: null == picture
picture: ? _value.picture
null == picture : picture // ignore: cast_nullable_to_non_nullable
? _value.picture as String,
: picture // ignore: cast_nullable_to_non_nullable
as String,
) )
as $Val, as $Val,
); );
@ -102,16 +100,14 @@ class __$$UserImplCopyWithImpl<$Res>
$Res call({Object? name = null, Object? picture = null}) { $Res call({Object? name = null, Object? picture = null}) {
return _then( return _then(
_$UserImpl( _$UserImpl(
name: name: null == name
null == name ? _value.name
? _value.name : name // ignore: cast_nullable_to_non_nullable
: name // ignore: cast_nullable_to_non_nullable as String,
as String, picture: null == picture
picture: ? _value.picture
null == picture : picture // ignore: cast_nullable_to_non_nullable
? _value.picture as String,
: picture // ignore: cast_nullable_to_non_nullable
as String,
), ),
); );
} }

@ -63,12 +63,9 @@ class BookingCreateUseCase {
return Result.error(activitiesResult.error); return Result.error(activitiesResult.error);
case Ok<List<Activity>>(): case Ok<List<Activity>>():
} }
final activities = final activities = activitiesResult.value
activitiesResult.value .where((activity) => itineraryConfig.activities.contains(activity.ref))
.where( .toList();
(activity) => itineraryConfig.activities.contains(activity.ref),
)
.toList();
_log.fine('Activities loaded (${activities.length})'); _log.fine('Activities loaded (${activities.length})');
// Check if dates are set // Check if dates are set

@ -67,26 +67,24 @@ class ActivitiesViewModel extends ChangeNotifier {
switch (resultActivities) { switch (resultActivities) {
case Ok(): case Ok():
{ {
_daytimeActivities = _daytimeActivities = resultActivities.value
resultActivities.value .where(
.where( (activity) => [
(activity) => [ TimeOfDay.any,
TimeOfDay.any, TimeOfDay.morning,
TimeOfDay.morning, TimeOfDay.afternoon,
TimeOfDay.afternoon, ].contains(activity.timeOfDay),
].contains(activity.timeOfDay), )
) .toList();
.toList();
_eveningActivities = resultActivities.value
_eveningActivities = .where(
resultActivities.value (activity) => [
.where( TimeOfDay.evening,
(activity) => [ TimeOfDay.night,
TimeOfDay.evening, ].contains(activity.timeOfDay),
TimeOfDay.night, )
].contains(activity.timeOfDay), .toList();
)
.toList();
_log.fine( _log.fine(
'Activities (daytime: ${_daytimeActivities.length}, ' 'Activities (daytime: ${_daytimeActivities.length}, '

@ -71,10 +71,9 @@ class _ActivitiesScreenState extends State<ActivitiesScreen> {
Expanded( Expanded(
child: Center( child: Center(
child: ErrorIndicator( child: ErrorIndicator(
title: title: AppLocalization.of(
AppLocalization.of( context,
context, ).errorWhileLoadingActivities,
).errorWhileLoadingActivities,
label: AppLocalization.of(context).tryAgain, label: AppLocalization.of(context).tryAgain,
onPressed: widget.viewModel.loadActivities.execute, onPressed: widget.viewModel.loadActivities.execute,
), ),
@ -171,10 +170,9 @@ class _BottomArea extends StatelessWidget {
), ),
FilledButton( FilledButton(
key: const Key(confirmButtonKey), key: const Key(confirmButtonKey),
onPressed: onPressed: viewModel.selectedActivities.isNotEmpty
viewModel.selectedActivities.isNotEmpty ? viewModel.saveActivities.execute
? viewModel.saveActivities.execute : null,
: null,
child: Text(AppLocalization.of(context).confirm), child: Text(AppLocalization.of(context).confirm),
), ),
], ],

@ -99,11 +99,10 @@ class _LoginScreenState extends State<LoginScreen> {
content: Text(AppLocalization.of(context).errorWhileLogin), content: Text(AppLocalization.of(context).errorWhileLogin),
action: SnackBarAction( action: SnackBarAction(
label: AppLocalization.of(context).tryAgain, label: AppLocalization.of(context).tryAgain,
onPressed: onPressed: () => widget.viewModel.login.execute((
() => widget.viewModel.login.execute(( _email.value.text,
_email.value.text, _password.value.text,
_password.value.text, )),
)),
), ),
), ),
); );

@ -50,8 +50,8 @@ class BookingViewModel extends ChangeNotifier {
Future<Result<void>> _createBooking() async { Future<Result<void>> _createBooking() async {
_log.fine('Loading booking'); _log.fine('Loading booking');
final itineraryConfig = final itineraryConfig = await _itineraryConfigRepository
await _itineraryConfigRepository.getItineraryConfig(); .getItineraryConfig();
switch (itineraryConfig) { switch (itineraryConfig) {
case Ok<ItineraryConfig>(): case Ok<ItineraryConfig>():
_log.fine('Loaded stored ItineraryConfig'); _log.fine('Loaded stored ItineraryConfig');

@ -90,18 +90,17 @@ class _Tags extends StatelessWidget {
child: Wrap( child: Wrap(
spacing: 6, spacing: 6,
runSpacing: 6, runSpacing: 6,
children: children: booking.destination.tags
booking.destination.tags .map(
.map( (tag) => TagChip(
(tag) => TagChip( tag: tag,
tag: tag, fontSize: 16,
fontSize: 16, height: 32,
height: 32, chipColor: chipColor,
chipColor: chipColor, onChipColor: Theme.of(context).colorScheme.onSurface,
onChipColor: Theme.of(context).colorScheme.onSurface, ),
), )
) .toList(),
.toList(),
), ),
); );
} }

@ -44,18 +44,16 @@ class _BookingScreenState extends State<BookingScreen> {
child: Scaffold( child: Scaffold(
floatingActionButton: ListenableBuilder( floatingActionButton: ListenableBuilder(
listenable: widget.viewModel, listenable: widget.viewModel,
builder: builder: (context, _) => FloatingActionButton.extended(
(context, _) => FloatingActionButton.extended( // Workaround for https://github.com/flutter/flutter/issues/115358#issuecomment-2117157419
// Workaround for https://github.com/flutter/flutter/issues/115358#issuecomment-2117157419 heroTag: null,
heroTag: null, key: const ValueKey('share-button'),
key: const ValueKey('share-button'), onPressed: widget.viewModel.booking != null
onPressed: ? widget.viewModel.shareBooking.execute
widget.viewModel.booking != null : null,
? widget.viewModel.shareBooking.execute label: Text(AppLocalization.of(context).shareTrip),
: null, icon: const Icon(Icons.share_outlined),
label: Text(AppLocalization.of(context).shareTrip), ),
icon: const Icon(Icons.share_outlined),
),
), ),
body: ListenableBuilder( body: ListenableBuilder(
// Listen to changes in both commands // Listen to changes in both commands

@ -35,12 +35,11 @@ abstract final class Dimens {
static const Dimens mobile = _DimensMobile(); static const Dimens mobile = _DimensMobile();
/// Get dimensions definition based on screen size /// Get dimensions definition based on screen size
factory Dimens.of(BuildContext context) => switch (MediaQuery.sizeOf( factory Dimens.of(BuildContext context) =>
context, switch (MediaQuery.sizeOf(context).width) {
).width) { > 600 && < 840 => desktop,
> 600 && < 840 => desktop, _ => mobile,
_ => mobile, };
};
} }
/// Mobile dimensions /// Mobile dimensions

@ -28,10 +28,9 @@ class CustomCheckbox extends StatelessWidget {
), ),
child: Material( child: Material(
borderRadius: BorderRadius.circular(24), borderRadius: BorderRadius.circular(24),
color: color: value
value ? Theme.of(context).colorScheme.primary
? Theme.of(context).colorScheme.primary : Colors.transparent,
: Colors.transparent,
child: SizedBox( child: SizedBox(
width: 24, width: 24,
height: 24, height: 24,

@ -93,31 +93,29 @@ class _HomeScreenState extends State<HomeScreen> {
), ),
SliverList.builder( SliverList.builder(
itemCount: widget.viewModel.bookings.length, itemCount: widget.viewModel.bookings.length,
itemBuilder: itemBuilder: (_, index) => _Booking(
(_, index) => _Booking( key: ValueKey(widget.viewModel.bookings[index].id),
key: ValueKey(widget.viewModel.bookings[index].id), booking: widget.viewModel.bookings[index],
booking: widget.viewModel.bookings[index], onTap: () => context.push(
onTap: Routes.bookingWithId(
() => context.push( widget.viewModel.bookings[index].id,
Routes.bookingWithId(
widget.viewModel.bookings[index].id,
),
),
confirmDismiss: (_) async {
// wait for command to complete
await widget.viewModel.deleteBooking.execute(
widget.viewModel.bookings[index].id,
);
// if command completed successfully, return true
if (widget.viewModel.deleteBooking.completed) {
// removes the dismissable from the list
return true;
} else {
// the dismissable stays in the list
return false;
}
},
), ),
),
confirmDismiss: (_) async {
// wait for command to complete
await widget.viewModel.deleteBooking.execute(
widget.viewModel.bookings[index].id,
);
// if command completed successfully, return true
if (widget.viewModel.deleteBooking.completed) {
// removes the dismissable from the list
return true;
} else {
// the dismissable stays in the list
return false;
}
},
),
), ),
], ],
); );

@ -61,12 +61,11 @@ class _Title extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ShaderMask( return ShaderMask(
blendMode: BlendMode.srcIn, blendMode: BlendMode.srcIn,
shaderCallback: shaderCallback: (bounds) => RadialGradient(
(bounds) => RadialGradient( center: Alignment.bottomLeft,
center: Alignment.bottomLeft, radius: 2,
radius: 2, colors: [Colors.purple.shade700, Colors.purple.shade400],
colors: [Colors.purple.shade700, Colors.purple.shade400], ).createShader(Rect.fromLTWH(0, 0, bounds.width, bounds.height)),
).createShader(Rect.fromLTWH(0, 0, bounds.width, bounds.height)),
child: Text( child: Text(
text, text,
style: GoogleFonts.rubik( style: GoogleFonts.rubik(

@ -67,13 +67,12 @@ class ResultsViewModel extends ChangeNotifier {
case Ok(): case Ok():
{ {
// If the result is Ok, update the list of destinations // If the result is Ok, update the list of destinations
_destinations = _destinations = result.value
result.value .where(
.where( (destination) =>
(destination) => destination.continent == _itineraryConfig!.continent,
destination.continent == _itineraryConfig!.continent, )
) .toList();
.toList();
_log.fine('Destinations (${_destinations.length}) loaded'); _log.fine('Destinations (${_destinations.length}) loaded');
} }
case Error(): case Error():

@ -42,8 +42,9 @@ class ResultCard extends StatelessWidget {
spacing: 4.0, spacing: 4.0,
runSpacing: 4.0, runSpacing: 4.0,
direction: Axis.horizontal, direction: Axis.horizontal,
children: children: destination.tags
destination.tags.map((e) => TagChip(tag: e)).toList(), .map((e) => TagChip(tag: e))
.toList(),
), ),
], ],
), ),

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save