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

I got carried away with Gemini and basically rewrote CI and the release
process for the new LLM reality. This work was largely completed by
Gemini.

- 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 #2499 
- 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/2723/head
Eric Windmill 3 weeks ago committed by GitHub
parent 0aa5415d5e
commit 2999d738b8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,11 @@
{
"mcpServers": {
"dart": {
"command": "dart",
"args": [
"mcp-server"
]
}
},
"contextFileName": "/.prompts/llm.md"
}

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

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

4
.gitignore vendored

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

@ -0,0 +1,63 @@
# Code Health and Style Guide Analysis
This document provides instructions for an AI assistant to analyze this repository against the Dart and Flutter style guide (`.prompts/llm.md`) and log opportunities for improvement.
## Workflow
The AI assistant must follow these steps exactly:
### 1. Setup
1. **Create Log Directory**: If it does not already exist, create a directory named `logs` in the repository root.
2. **Create Log File**: Create a new file inside the `logs` directory named `YYYY-MM-DD_HH-MM-SS-freshness-scores.md`, where `YYYY-MM-DD_HH-MM-SS` is the current timestamp.
### 2. Project Identification
1. **Find Projects**: Identify all sample projects by searching for `pubspec.yaml` files within the repository. Each directory containing a `pubspec.yaml` should be considered a separate project.
2. **Create a Queue**: Compile a list of the absolute paths to these project directories to process them one by one.
### 3. Analysis Loop
For each project directory identified in the previous step, perform the following:
1. **Check for Dart Files**:
* Verify that the project directory contains at least one `.dart` file.
* If not, add an entry to the log file stating that the project was skipped for this reason and proceed to the next project.
2. **Analyze Git History**:
* Run `git log` to find the most recent commit to a `.dart` file within that project's directory made by a human (i.e., not a bot).
* **Command**:
```bash
git log -1 --author='^(?!.*bot).*$' --pretty="format:%ad" --date=short -- ./**/*.dart
```
*(Note: This command should be run from within the project's directory).*
* **Handle No Commits**: If the command returns no output, note "No human commits found" for the log. If there are no commits at all, note that as well.
3. **Read and Assess Code**:
* Read all `.dart` files within the project directory (recursively).
* Compare the code against the rules and patterns defined in `.prompts/llm.md`.
* The assessment must focus on:
* Code organization and structure.
* Adherence to naming conventions.
* Proper use of Flutter and Dart patterns (e.g., `const` constructors, collection literals).
* State management best practices.
* Clarity, readability, and documentation.
4. **Log Findings**:
* Append a new entry to the log file using the following Markdown template. Ensure all fields are filled out.
```markdown
---
**Sample**: `path/to/sample`
**Last Human Commit**: `YYYY-MM-DD` or "No human commits found"
**Assessment Date**: `YYYY-MM-DD`
**Summary of Style Guide Adherence**:
A brief, one-paragraph summary of how well the project adheres to the style guide. Mention its strengths and weaknesses in general terms.
**Opportunities for Improvement**:
- A concrete, actionable bullet point describing a specific area for improvement.
- Another actionable bullet point.
- A third bullet point, if applicable. Focus on providing clear, specific, and helpful recommendations.
---
```

@ -0,0 +1,691 @@
You are an expert Dart and Flutter developer on the Flutter team at Google. Your code must adhere to this style guide.
## Core Philosophy
- **Follow Effective Dart guidelines.**
- **Optimize for readability**: Write code that is easy to understand and maintain
- **Write detailed documentation**: Every public API should be well-documented
- **Keep one source of truth**: Avoid duplicating state across your application
- **Design APIs from the developer's perspective**: Consider how the API will be used
- **Create useful error messages**: Error messages should guide developers toward solutions
- **Write tests first**: When fixing bugs, write failing tests first, then fix the bug
- **Avoid workarounds**: Take time to fix problems correctly rather than implementing temporary solutions
## Naming Conventions
### Identifier Types (Official Dart Guidelines)
#### UpperCamelCase
- **Classes**: `MyWidget`, `UserRepository`, `HttpClient`
- **Enum types**: `ButtonType`, `AnimationState`, `ConnectionState`
- **Typedefs**: `EventCallback`, `ValidatorFunction`
- **Type parameters**: `<T>`, `<K, V>`, `<TModel>`
- **Extensions**: `StringExtension`, `MyFancyList`, `SmartIterable`
#### lowerCamelCase
- **Variables**: `userName`, `isLoading`, `itemCount`
- **Parameters**: `onPressed`, `itemBuilder`, `scrollDirection`
- **Class members**: `_privateField`, `publicMethod`
- **Top-level functions**: `buildWidget`, `validateInput`
- **Constants**: `defaultPadding`, `maxRetries`, `pi` (prefer over SCREAMING_CAPS)
#### lowercase_with_underscores
- **Packages**: `my_package`, `http_client`
- **Directories**: `lib/widgets/custom`, `test/unit_tests`
- **Source files**: `user_profile_widget.dart`, `file_system.dart`
- **Import prefixes**: `import 'dart:math' as math;`, `import 'package:foo/foo.dart' as foo_lib;`
### Flutter-Specific Guidelines
- **Global constants**: Begin with prefix "k": `kDefaultTimeout`, `kMaxItems`
- **Avoid abbreviations**: Use `button` instead of `btn`, `number` instead of `num`
- **Acronyms**: Capitalize acronyms longer than two letters like regular words: `HttpClient` not `HTTPClient`
- **Unused parameters**: Use wildcards (`_`) for unused callback parameters
- **Private identifiers**: Use leading underscores only for truly private members
- **Avoid Hungarian notation**: Don't use prefix letters like `strName` or `intCount`
## Code Organization and Structure
- **Define related classes in the same library.**
- **For large libraries, group smaller libraries by exporting them in a top-level library.**
- **Group related libraries in the same folder.**
### Import Ordering (Strict Dart Convention)
```dart
// 1. Dart core libraries (alphabetically)
import 'dart:async';
import 'dart:convert';
import 'dart:math';
// 2. Flutter and package imports (alphabetically)
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:provider/provider.dart';
// 3. Relative imports (alphabetically)
import '../models/user.dart';
import '../widgets/custom_button.dart';
import 'user_repository.dart';
// 4. Exports (if any, in separate section)
export 'src/my_library.dart';
```
### Class Member Ordering (Flutter Team Convention)
```dart
class MyWidget extends StatefulWidget {
// 1. Constructors first
const MyWidget({
super.key,
required this.title,
this.isEnabled = true,
});
// 2. Public constants
static const double kDefaultHeight = 48.0;
// 3. Public fields
final String title;
final bool isEnabled;
// 4. Private constants
static const double _defaultPadding = 16.0;
// 5. Private fields
String? _cachedValue;
// 6. Getters and setters
bool get isDisabled => !isEnabled;
// 7. Public methods
@override
State<MyWidget> createState() => _MyWidgetState();
// 8. Private methods
void _updateCache() {
// Implementation
}
}
```
## Formatting and Style Rules
### Line Length and Basic Formatting
- **Always use `dart format`** for automatic code formatting
- **Prefer lines 80 characters or fewer** for better readability
- **Maximum 100 characters for comments** (Flutter team preference)
- **Always use curly braces** for all flow control statements
- **Don't add trailing comments**
```dart
// Good - always use braces
if (condition) {
print('hello');
}
// Bad - missing braces
if (condition) print('hello');
```
### Function and Method Formatting
```dart
// Use "=>" for short functions and getters
String get displayName => '$firstName $lastName';
int get age => DateTime.now().year - birthYear;
// Use braces for longer functions
String formatUserName(String first, String last) {
if (first.isEmpty && last.isEmpty) {
return 'Anonymous';
}
return '$first $last'.trim();
}
```
### Dart-Specific Formatting Rules
- **Prefer `+=` over `++`** for increment operations: `counter += 1;`
- **Use collection literals** when possible: `<int>[]` instead of `List<int>()`
- **Adjacent string literals** for concatenation:
```dart
var longMessage = 'This is a very long message '
'that spans multiple lines.';
```
## Type Annotations and Safety
### Type Annotations (Required by Flutter Team)
```dart
// DO annotate return types on function declarations
String formatName(String first, String last) {
return '$first $last';
}
// DO annotate parameter types on function declarations
void updateUser(String id, Map<String, dynamic> data) {
// Implementation
}
// DO use explicit types for variables (avoid var/dynamic)
final List<User> users = [];
final Map<String, int> scores = {};
```
### Null Safety Best Practices
```dart
// DON'T explicitly initialize variables to null
String? name; // Good
String? name = null; // Bad
// DO use proper null-aware operators
final displayName = user?.name ?? 'Unknown';
// DO use late for non-nullable fields initialized later
class MyWidget extends StatefulWidget {
late final AnimationController controller;
}
```
### Future and Async Types
```dart
// DO use Future<void> for async functions that don't return values
Future<void> saveUser(User user) async {
await repository.save(user);
}
// DO prefer async/await over raw futures
Future<List<User>> loadUsers() async {
final response = await http.get('/api/users');
return parseUsers(response.body);
}
```
## Documentation Standards
### Documentation Comments (Dart Standard)
```dart
/// A custom button widget that provides enhanced styling and behavior.
///
/// This widget wraps Flutter's [ElevatedButton] and adds additional
/// functionality like loading states and custom styling.
///
/// The [onPressed] callback is called when the button is tapped.
/// Set [isEnabled] to false to disable the button.
///
/// Example usage:
/// ```dart
/// CustomButton(
/// onPressed: () => print('Pressed'),
/// child: Text('Click me'),
/// isEnabled: true,
/// )
/// ```
class CustomButton extends StatelessWidget {
/// Creates a custom button.
///
/// The [onPressed] and [child] parameters are required.
/// The [isEnabled] parameter defaults to true.
const CustomButton({
super.key,
required this.onPressed,
required this.child,
this.isEnabled = true,
});
/// Called when the button is pressed.
final VoidCallback? onPressed;
/// The widget to display inside the button.
final Widget child;
/// Whether the button is enabled for interaction.
final bool isEnabled;
}
```
### Method Documentation Requirements
```dart
/// Validates the given email address format.
///
/// Returns `true` if the [email] is valid according to RFC standards.
/// Returns `false` if the format is invalid.
///
/// Throws [ArgumentError] if [email] is null or empty.
///
/// Example:
/// ```dart
/// final isValid = validateEmail('user@example.com'); // true
/// ```
bool validateEmail(String email) {
if (email.isEmpty) {
throw ArgumentError('Email cannot be empty');
}
return RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(email);
}
```
## Flutter-Specific Patterns
- **Prefer composition over inheritance.**
- **Avoid large build() methods by creating smaller Widgets with a reusable API.**
- **Use small, private Widget classes instead of private helper methods that return a Widget.**
- **Use lazy lists wherever possible using ListView.builder.**
### Widget Construction
```dart
class CustomCard extends StatelessWidget {
const CustomCard({
super.key,
required this.title,
required this.content,
this.elevation = 2.0,
this.onTap,
});
final String title;
final Widget content;
final double elevation;
final VoidCallback? onTap;
@override
Widget build(BuildContext context) {
return Card(
elevation: elevation,
child: InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 8.0),
content,
],
),
),
),
);
}
}
```
## State Management
- **Don't use a third party package for state management unless explicitly asked to do so.**
- **Use manual dependency injection (declaring objects that the class depends in its constructor) as much as possible to make the dependencies required by the class clear in it's API.**
- **If asked to use Provider, use it for app-level objects that are used often.**
- **Use Model-View-ViewModel for application architecture.**
- **Use ChangeNotifier or a class with ValueNotifiers for ViewModel classes.**
- **Use a ListenableBuilder to listen to changes to the ViewModel.**
- **Use a StatefulWidget for widgets that are reusable or single-purpose, and don't necessarily require a MVVM architecture.**
## Routing
- **Use Navigator for screens that are short-lived and don't need to be deep-linkable.**
## Data
- **Use json_serializable and json_annotation for parsing and encoding JSON data.**
- **Use fieldRename: FieldRename.snake to encode data with snake case.**
## Code Generation
- **Use build_runner for any generated code in the app.**
## String and Collection Best Practices
### String Interpolation and Formatting
```dart
// PREFER using interpolation to compose strings
final name = 'John Doe';
final age = 25;
final message = 'Hello, $name! You are $age years old.';
final calculation = 'Next year you will be ${age + 1}.';
// DO use adjacent strings for long literals
const longText = 'This is a very long text that '
'spans multiple lines for better '
'readability in the source code.';
```
### Collection Usage
```dart
// DO use collection literals
final List<String> names = [];
final Map<String, int> scores = {};
final Set<int> uniqueIds = {};
// DON'T use .length to check if empty
if (names.isEmpty) { // Good
print('No names');
}
if (names.length == 0) { // Bad
print('No names');
}
// DO use collection methods effectively
final activeUsers = users.where((user) => user.isActive).toList();
final userNames = users.map((user) => user.name).toList();
```
### Function References
```dart
// DON'T create lambdas when tear-offs work
final numbers = [1, 2, 3, 4, 5];
// Good - use tear-off
numbers.forEach(print);
// Bad - unnecessary lambda
numbers.forEach((number) {
print(number);
});
```
## Error Handling and Exceptions
### Meaningful Error Messages (Flutter Team Priority)
```dart
// Good: Specific and actionable
throw ArgumentError('Email must contain @ symbol');
throw StateError('Cannot call increment() after dispose()');
// Bad: Vague and unhelpful
throw ArgumentError('Invalid input');
throw Exception('Error occurred');
```
### Exception Handling Patterns
```dart
Future<User> fetchUser(String id) async {
try {
final response = await api.getUser(id);
return User.fromJson(response.data);
} on NetworkException catch (e) {
throw UserFetchException('Failed to fetch user: ${e.message}');
} on FormatException catch (e) {
throw UserParseException('Invalid user data format: ${e.message}');
} catch (e) {
throw UserFetchException('Unexpected error: ${e.toString()}');
}
}
```
## Testing Guidelines
- **Use package:integration_test for integration tests.**
- **Use package:checks instead of matchers from package:test or package:matcher.**
### Widget Testing
```dart
testWidgets('CustomButton should call onPressed when tapped', (tester) async {
bool wasPressed = false;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: CustomButton(
onPressed: () => wasPressed = true,
child: const Text('Test Button'),
),
),
),
);
await tester.tap(find.byType(CustomButton));
await tester.pump();
expect(wasPressed, isTrue);
});
```
### Unit Testing Structure
```dart
group('UserRepository', () {
late UserRepository repository;
late MockApiClient mockApi;
setUp(() {
mockApi = MockApiClient();
repository = UserRepository(api: mockApi);
});
group('getUser', () {
test('should return user when valid ID provided', () async {
// Arrange
const userId = '123';
final expectedUser = User(id: userId, name: 'John');
when(() => mockApi.getUser(userId))
.thenAnswer((_) async => expectedUser.toJson());
// Act
final user = await repository.getUser(userId);
// Assert
expect(user.id, equals(userId));
expect(user.name, equals('John'));
});
test('should throw exception when user not found', () async {
// Arrange
const userId = 'invalid';
when(() => mockApi.getUser(userId))
.thenThrow(NotFoundException());
// Act & Assert
expect(
() => repository.getUser(userId),
throwsA(isA<UserNotFoundException>()),
);
});
});
});
```
## Advanced Dart Patterns
- **Use Patterns and pattern-matching features where possible.**
### Immutability and Data Classes
```dart
class User {
const User({
required this.id,
required this.name,
required this.email,
this.isActive = true,
});
final String id;
final String name;
final String email;
final bool isActive;
// Use copyWith for immutable updates
User copyWith({
String? id,
String? name,
String? email,
bool? isActive,
}) {
return User(
id: id ?? this.id,
name: name ?? this.name,
email: email ?? this.email,
isActive: isActive ?? this.isActive,
);
}
// Override equality
@override
bool operator ==(Object other) =>
other is User &&
runtimeType == other.runtimeType &&
id == other.id;
@override
int get hashCode => id.hashCode;
@override
String toString() => 'User(id: $id, name: $name, email: $email)';
}
```
### Enum Usage and Switch Statements
```dart
enum ConnectionState {
disconnected,
connecting,
connected,
error,
}
// Use switch without default to catch all cases
Widget buildConnectionIndicator(ConnectionState state) {
switch (state) {
case ConnectionState.disconnected:
return const Icon(Icons.wifi_off, color: Colors.grey);
case ConnectionState.connecting:
return const CircularProgressIndicator();
case ConnectionState.connected:
return const Icon(Icons.wifi, color: Colors.green);
case ConnectionState.error:
return const Icon(Icons.error, color: Colors.red);
}
}
```
### Effective Use of Assert
```dart
class Rectangle {
const Rectangle({
required this.width,
required this.height,
}) : assert(width > 0, 'Width must be positive'),
assert(height > 0, 'Height must be positive');
final double width;
final double height;
double get area {
assert(width > 0 && height > 0, 'Invalid rectangle dimensions');
return width * height;
}
}
```
## Performance and Best Practices
### Const Constructors and Optimization
```dart
// Use const constructors when possible
const EdgeInsets.all(16.0);
const SizedBox(height: 8.0);
// Create const widgets for better performance
class LoadingIndicator extends StatelessWidget {
const LoadingIndicator({super.key});
@override
Widget build(BuildContext context) {
return const Center(
child: CircularProgressIndicator(),
);
}
}
```
### Efficient Widget Building
```dart
class ProductList extends StatelessWidget {
const ProductList({
super.key,
required this.products,
});
final List<Product> products;
@override
Widget build(BuildContext context) {
// Don't do heavy computation in build method
return ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return ProductTile(
key: ValueKey(product.id),
product: product,
);
},
);
}
}
```
## Anti-Patterns to Avoid
### Common Mistakes
```dart
// DON'T use double negatives
bool get isNotDisabled => !disabled; // Confusing
// DO use positive naming
bool get isEnabled => !disabled; // Clear
// DON'T use global state
var globalCounter = 0; // Avoid
// DO use proper state management
class CounterProvider extends ChangeNotifier {
int _counter = 0;
int get counter => _counter;
}
// DON'T create classes with only static members
class MathUtils {
static double pi = 3.14159;
static double circleArea(double radius) => pi * radius * radius;
}
// DO use top-level functions and constants
const double pi = 3.14159;
double circleArea(double radius) => pi * radius * radius;
// DON'T avoid using APIs as intended
class TimeSlot {
TimeSlot(this.start, this.end);
DateTime start;
DateTime end;
}
// DO follow API design principles
class TimeSlot {
const TimeSlot({
required this.start,
required this.end,
}) : assert(start.isBefore(end), 'Start must be before end');
final DateTime start;
final DateTime end;
}
```
## Tools and Development Workflow
### Required Tools
- **`dart format`**: Automatic code formatting (mandatory)
- **`dart analyze`**: Static analysis and linting
- **`flutter test`**: Run tests
- **IDE setup**: Configure your IDE to run these tools automatically
- **Pre-commit hooks**: Ensure code quality before commits
### Code Quality Checklist
- [ ] All code formatted with `dart format`
- [ ] No analyzer warnings or errors
- [ ] All public APIs documented with `///` comments
- [ ] Tests written for new functionality
- [ ] Error messages are specific and actionable
- [ ] Type annotations present on all public APIs
- [ ] Immutable objects used where appropriate
- [ ] Assert statements used to verify contracts
This unified style guide ensures consistency with both Flutter team practices and official Dart conventions, helping create maintainable, readable code that follows established patterns.

@ -0,0 +1,93 @@
You are an AI developer specializing in Dart and Flutter. Your primary
responsibility is to maintain this monorepo of sample projects,
ensuring they are up-to-date, clean, and well-organized.
This workflow is triggered when a new Flutter/Dart version is
released. Follow these steps precisely:
1. Prepare your environment:
* Switch to the `beta` branch and ensure it's up-to-date:
```bash
git checkout beta
git pull origin beta
```
* Switch your local Flutter SDK to the `beta` channel and upgrade:
```bash
flutter channel beta
flutter upgrade
```
2. Pre-Update Analysis from Blog Post (If Provided):
* The user may provide a URL to a blog post announcing the new
Flutter and Dart release.
* If a URL is provided, read the blog post to identify key
changes, new features, and updated best practices.
* Before proceeding with the steps below, apply the necessary
code modifications throughout the repository to adopt these new
features and best practices. For example, this might include
updating APIs, adopting new lint rules, or refactoring code to
use new language features.
3. Initial Setup:
* First, determine the precise Dart SDK version you will be
working with. Execute the command `flutter --version --machine`.
* Parse the JSON output to find the value of dartSdkVersion. You
will need the version number (e.g., 3.9.0). Let's call this
DART_VERSION.
* Next, read the pubspec.yaml file at the root of the monorepo.
* Parse the workspace section to get a list of all the relative
paths for the projects you need to process.
4. Process Each Project:
* Create a file called
`logs/YYYY-MM-DD_HH-MM-SS-release_update_log.txt`, but replace
YYYY-MM-DD_HH-MM-SS with the current date/time.
* Iterate through each project path you discovered in the
workspace.
* For each project, perform the following actions in its
directory. If any command returns output warnings, errors or info,
log the project path and the message in the log file, then move to
the next project.
5. Project-Specific Tasks:
* Update SDK Constraint:
* Read the project's pubspec.yaml file.
* Find the environment.sdk key.
* Update its value to ^DART_VERSION-0 (e.g., ^3.9.0-0).
* Save the modified pubspec.yaml file.
* Run Quality Checks:
* Run dart analyze --fatal-infos --fatal-warnings.
* Run dart format . to ensure the code is correctly formatted.
* Run Tests:
* Check if a test directory exists in the project.
* Exception: Do not run tests for the project named
material_3_demo.
* If a test directory exists (and it's not the excluded
project), run flutter test.
6. Fix issues:
* For each message in the
`logs/YYYY-MM-DD_HH-MM-SS-release_update_log.txt` file, attempt
to fix the problem. After 30 seconds of being unable to fix it,
move onto the next issue.
* If you fix the issue successfully, remove the message from the
log file.
* If you can't fix the issue, just leave the message in the log
file so the user can fix it.
7. Final Report:
* After processing all projects, generate a summary report.
* The report must include:
* The total number of projects processed.
* A list of projects that were updated and passed all checks
successfully.
* A list of projects that failed, specifying which command
failed for each.
8. Create Pull Request:
* After generating the report, create a pull request.
* Use the `gh` CLI tool for this purpose.
* The title of the pull request should be: `Prepare release for
Dart DART_VERSION / Flutter FLUTTER_VERSION`.
* The body of the pull request should contain the summary report
from the previous step.

@ -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
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.

@ -23,7 +23,12 @@ class Cell extends StatefulWidget {
class _CellState extends State<Cell> with WidgetsBindingObserver {
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;
Random? _random;
@ -82,7 +87,10 @@ class _CellState extends State<Cell> with WidgetsBindingObserver {
builder: (context) {
return Card(
// Mimic the platform Material look.
margin: const EdgeInsets.symmetric(horizontal: 36, vertical: 24),
margin: const EdgeInsets.symmetric(
horizontal: 36,
vertical: 24,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
@ -112,22 +120,22 @@ class _CellState extends State<Cell> with WidgetsBindingObserver {
child: StreamBuilder<AccelerometerEvent>(
// Don't continuously rebuild for nothing when the
// cell isn't visible.
stream:
appLifecycleState == AppLifecycleState.resumed
? accelerometerEventStream()
: Stream.value(defaultPosition),
stream: appLifecycleState == AppLifecycleState.resumed
? accelerometerEventStream()
: Stream.value(defaultPosition),
initialData: defaultPosition,
builder: (context, snapshot) {
return Transform(
// Figure out the phone's orientation relative
// to gravity's direction. Ignore the z vector.
transform: Matrix4.rotationX(
snapshot.data!.y / gravity * pi / 2,
)..multiply(
Matrix4.rotationY(
snapshot.data!.x / gravity * pi / 2,
),
),
transform:
Matrix4.rotationX(
snapshot.data!.y / gravity * pi / 2,
)..multiply(
Matrix4.rotationY(
snapshot.data!.x / gravity * pi / 2,
),
),
alignment: Alignment.center,
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.
version: 1.0.0+1
resolution: workspace
environment:
sdk: ^3.7.0-0
sdk: ^3.8.1-0
dependencies:
flutter:
sdk: flutter
provider: ^6.0.2
url_launcher: ^6.0.20
sensors_plus: ^5.0.1
provider: ^6.1.5
url_launcher: ^6.3.2
sensors_plus: ^6.1.1
dev_dependencies:
analysis_defaults:

@ -6,7 +6,7 @@
// tree, read text, and verify that the values of widget properties are correct.
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:provider/provider.dart';

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

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

@ -20,11 +20,15 @@ void main() {
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();
await tester.pumpWidget(
MaterialApp(home: BookDetail(book: Book(), hostApi: mockHostApi)),
MaterialApp(
home: BookDetail(book: Book(), hostApi: mockHostApi),
),
);
await tester.tap(find.byIcon(Icons.check));

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

@ -3,7 +3,7 @@
// found in the LICENSE file.
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:provider/provider.dart';

@ -3,7 +3,7 @@
// found in the LICENSE file.
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
// https://pub.dev/packages/espresso for details.

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

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

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

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

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

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

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

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

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

@ -21,7 +21,8 @@ const double windowWidth = 480;
const double windowHeight = 854;
void setupWindow() {
if (!kIsWeb && (Platform.isWindows || Platform.isLinux || Platform.isMacOS)) {
if (!kIsWeb &&
(Platform.isWindows || Platform.isLinux || Platform.isMacOS)) {
WidgetsFlutterBinding.ensureInitialized();
setWindowTitle('Animation Samples');
setWindowMinSize(const Size(windowWidth, windowHeight));
@ -43,7 +44,11 @@ class Demo {
final String route;
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 = [

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

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

@ -74,7 +74,10 @@ class _TweenSequenceDemoState extends State<TweenSequenceDemo>
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(
appBar: AppBar(
title: const Text('AnimatedList'),
actions: [IconButton(icon: const Icon(Icons.add), onPressed: addUser)],
actions: [
IconButton(icon: const Icon(Icons.add), onPressed: addUser),
],
),
body: SafeArea(
child: AnimatedList(

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

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

@ -15,8 +15,9 @@ class CarouselDemo extends StatelessWidget {
'assets/eat_sydney_sm.jpg',
];
final List<Widget> images =
fileNames.map((file) => Image.asset(file, fit: BoxFit.cover)).toList();
final List<Widget> images = fileNames
.map((file) => Image.asset(file, fit: BoxFit.cover))
.toList();
@override
Widget build(context) {
@ -77,29 +78,32 @@ class _CarouselState extends State<Carousel> {
},
controller: _controller,
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,
),
DropdownButton<CurveChoice>(
items:
curves.map((curve) {
return DropdownMenuItem<CurveChoice>(
value: curve,
child: Text(curve.name),
);
}).toList(),
items: curves.map((curve) {
return DropdownMenuItem<CurveChoice>(
value: curve,
child: Text(curve.name),
);
}).toList(),
onChanged: (newCurve) {
if (newCurve != null) {
setState(() {
@ -114,13 +113,12 @@ class _CurvedAnimationDemoState extends State<CurvedAnimationDemo>
style: Theme.of(context).textTheme.titleLarge,
),
DropdownButton<CurveChoice>(
items:
curves.map((curve) {
return DropdownMenuItem<CurveChoice>(
value: curve,
child: Text(curve.name),
);
}).toList(),
items: curves.map((curve) {
return DropdownMenuItem<CurveChoice>(
value: curve,
child: Text(curve.name),
);
}).toList(),
onChanged: (newCurve) {
if (newCurve != null) {
setState(() {

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

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

@ -29,10 +29,12 @@ class Grid extends StatelessWidget {
),
itemBuilder: (context, index) {
return (index >= 20)
? const SmallCard(imageAssetName: 'assets/eat_cape_town_sm.jpg')
? const SmallCard(
imageAssetName: 'assets/eat_cape_town_sm.jpg',
)
: 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);
return Stack(
children: [PositionedTransition(rect: rectAnimation, child: child)],
children: [
PositionedTransition(rect: rectAnimation, child: child),
],
);
},
);

@ -15,10 +15,15 @@ class HeroAnimationDemo extends StatelessWidget {
body: GestureDetector(
child: Hero(
tag: 'hero-page-child',
child: _createHeroContainer(size: 50.0, color: Colors.grey.shade300),
child: _createHeroContainer(
size: 50.0,
color: Colors.grey.shade300,
),
),
onTap:
() => Navigator.of(context).push<void>(
onTap: () =>
Navigator.of(
context,
).push<void>(
MaterialPageRoute(builder: (context) => const HeroPage()),
),
),
@ -52,7 +57,9 @@ StatelessWidget _createHeroContainer({
height: size,
width: size,
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),
child: const FlutterLogo(),
);

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

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

@ -2,15 +2,16 @@ name: animations
description: A new Flutter project.
version: 1.0.0+1
publish_to: none
resolution: workspace
environment:
sdk: ^3.7.0-0
sdk: ^3.9.0-0
dependencies:
flutter:
sdk: flutter
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
git:
url: https://github.com/google/flutter-desktop-embedding.git
@ -22,7 +23,6 @@ dev_dependencies:
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
assets:

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

@ -24,7 +24,10 @@ void main() {
await tester.pumpAndSettle();
// 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 {
@ -36,7 +39,10 @@ void main() {
// Swipe out all cards.
for (var i = 0; i < totalCards; i++) {
// 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();
}

@ -18,7 +18,11 @@ void main() {
expect(imageList.length, 2);
// 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();
// Get the images available on the screen after swipe.

@ -22,7 +22,10 @@ void main() {
// The size of ExpandCard must change once tapped.
// 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 {

@ -33,7 +33,9 @@ void main() {
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());
// Tap on the ink well at index 0.

@ -58,7 +58,9 @@ void main() {
// Final color should not be same as initial color.
expect(
(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());
// Get the initial Screen.
final initialScreen = tester.firstWidget(find.byType(HeroAnimationDemo));
final initialScreen = tester.firstWidget(
find.byType(HeroAnimationDemo),
);
// Tap on the 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.
// `--input` is the original asset file that this program should transform.
// `--output` is where flutter expects the transformation output to be written to.
final parser =
ArgParser()
..addOption(inputOptionName, mandatory: true, abbr: 'i')
..addOption(outputOptionName, mandatory: true, abbr: 'o');
final parser = ArgParser()
..addOption(inputOptionName, mandatory: true, abbr: 'i')
..addOption(outputOptionName, mandatory: true, abbr: 'o');
ArgResults argResults = parser.parse(arguments);
final String inputFilePath = argResults[inputOptionName];

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

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

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

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

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

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

@ -9,10 +9,9 @@ import 'package:shelf_router/shelf_router.dart';
int count = 0;
// Configure routes.
final _router =
Router()
..post('/', _incrementHandler)
..get('/', _getValueHandler);
final _router = Router()
..post('/', _incrementHandler)
..get('/', _getValueHandler);
Future<Response> _incrementHandler(Request request) async {
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.
version: 1.0.0
publish_to: "none"
resolution: workspace
environment:
sdk: ^3.7.0-0
sdk: ^3.9.0-0
dependencies:
args: ^2.0.0
@ -14,6 +15,8 @@ dependencies:
path: ../shared
dev_dependencies:
analysis_defaults:
path: ../../analysis_defaults
http: ^1.0.0
lints: ^6.0.0
test: ^1.15.0

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

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

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

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

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

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

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

@ -91,36 +91,30 @@ class _$BookingApiModelCopyWithImpl<$Res, $Val extends BookingApiModel>
}) {
return _then(
_value.copyWith(
id:
freezed == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int?,
startDate:
null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
endDate:
null == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime,
name:
null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
destinationRef:
null == destinationRef
? _value.destinationRef
: destinationRef // ignore: cast_nullable_to_non_nullable
as String,
activitiesRef:
null == activitiesRef
? _value.activitiesRef
: activitiesRef // ignore: cast_nullable_to_non_nullable
as List<String>,
id: freezed == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int?,
startDate: null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
endDate: null == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
destinationRef: null == destinationRef
? _value.destinationRef
: destinationRef // ignore: cast_nullable_to_non_nullable
as String,
activitiesRef: null == activitiesRef
? _value.activitiesRef
: activitiesRef // ignore: cast_nullable_to_non_nullable
as List<String>,
)
as $Val,
);
@ -169,36 +163,30 @@ class __$$BookingApiModelImplCopyWithImpl<$Res>
}) {
return _then(
_$BookingApiModelImpl(
id:
freezed == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int?,
startDate:
null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
endDate:
null == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime,
name:
null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
destinationRef:
null == destinationRef
? _value.destinationRef
: destinationRef // ignore: cast_nullable_to_non_nullable
as String,
activitiesRef:
null == activitiesRef
? _value._activitiesRef
: activitiesRef // ignore: cast_nullable_to_non_nullable
as List<String>,
id: freezed == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int?,
startDate: null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
endDate: null == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
destinationRef: null == destinationRef
? _value.destinationRef
: destinationRef // ignore: cast_nullable_to_non_nullable
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),
name: json['name'] as String,
destinationRef: json['destinationRef'] as String,
activitiesRef:
(json['activitiesRef'] as List<dynamic>).map((e) => e as String).toList(),
activitiesRef: (json['activitiesRef'] as List<dynamic>)
.map((e) => e as String)
.toList(),
);
Map<String, dynamic> _$$BookingApiModelImplToJson(

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

@ -75,26 +75,22 @@ class _$UserApiModelCopyWithImpl<$Res, $Val extends UserApiModel>
}) {
return _then(
_value.copyWith(
id:
null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
name:
null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
email:
null == email
? _value.email
: email // ignore: cast_nullable_to_non_nullable
as String,
picture:
null == picture
? _value.picture
: picture // ignore: cast_nullable_to_non_nullable
as String,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
email: null == email
? _value.email
: email // ignore: cast_nullable_to_non_nullable
as String,
picture: null == picture
? _value.picture
: picture // ignore: cast_nullable_to_non_nullable
as String,
)
as $Val,
);
@ -134,26 +130,22 @@ class __$$UserApiModelImplCopyWithImpl<$Res>
}) {
return _then(
_$UserApiModelImpl(
id:
null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
name:
null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
email:
null == email
? _value.email
: email // ignore: cast_nullable_to_non_nullable
as String,
picture:
null == picture
? _value.picture
: picture // ignore: cast_nullable_to_non_nullable
as String,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
email: null == email
? _value.email
: email // ignore: cast_nullable_to_non_nullable
as String,
picture: 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(
_value.copyWith(
name:
null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
description:
null == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String,
locationName:
null == locationName
? _value.locationName
: locationName // ignore: cast_nullable_to_non_nullable
as String,
duration:
null == duration
? _value.duration
: duration // ignore: cast_nullable_to_non_nullable
as int,
timeOfDay:
null == timeOfDay
? _value.timeOfDay
: timeOfDay // ignore: cast_nullable_to_non_nullable
as TimeOfDay,
familyFriendly:
null == familyFriendly
? _value.familyFriendly
: familyFriendly // ignore: cast_nullable_to_non_nullable
as bool,
price:
null == price
? _value.price
: price // ignore: cast_nullable_to_non_nullable
as int,
destinationRef:
null == destinationRef
? _value.destinationRef
: destinationRef // ignore: cast_nullable_to_non_nullable
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,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
description: null == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String,
locationName: null == locationName
? _value.locationName
: locationName // ignore: cast_nullable_to_non_nullable
as String,
duration: null == duration
? _value.duration
: duration // ignore: cast_nullable_to_non_nullable
as int,
timeOfDay: null == timeOfDay
? _value.timeOfDay
: timeOfDay // ignore: cast_nullable_to_non_nullable
as TimeOfDay,
familyFriendly: null == familyFriendly
? _value.familyFriendly
: familyFriendly // ignore: cast_nullable_to_non_nullable
as bool,
price: null == price
? _value.price
: price // ignore: cast_nullable_to_non_nullable
as int,
destinationRef: null == destinationRef
? _value.destinationRef
: destinationRef // ignore: cast_nullable_to_non_nullable
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,
);
@ -215,56 +205,46 @@ class __$$ActivityImplCopyWithImpl<$Res>
}) {
return _then(
_$ActivityImpl(
name:
null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
description:
null == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String,
locationName:
null == locationName
? _value.locationName
: locationName // ignore: cast_nullable_to_non_nullable
as String,
duration:
null == duration
? _value.duration
: duration // ignore: cast_nullable_to_non_nullable
as int,
timeOfDay:
null == timeOfDay
? _value.timeOfDay
: timeOfDay // ignore: cast_nullable_to_non_nullable
as TimeOfDay,
familyFriendly:
null == familyFriendly
? _value.familyFriendly
: familyFriendly // ignore: cast_nullable_to_non_nullable
as bool,
price:
null == price
? _value.price
: price // ignore: cast_nullable_to_non_nullable
as int,
destinationRef:
null == destinationRef
? _value.destinationRef
: destinationRef // ignore: cast_nullable_to_non_nullable
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,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
description: null == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String,
locationName: null == locationName
? _value.locationName
: locationName // ignore: cast_nullable_to_non_nullable
as String,
duration: null == duration
? _value.duration
: duration // ignore: cast_nullable_to_non_nullable
as int,
timeOfDay: null == timeOfDay
? _value.timeOfDay
: timeOfDay // ignore: cast_nullable_to_non_nullable
as TimeOfDay,
familyFriendly: null == familyFriendly
? _value.familyFriendly
: familyFriendly // ignore: cast_nullable_to_non_nullable
as bool,
price: null == price
? _value.price
: price // ignore: cast_nullable_to_non_nullable
as int,
destinationRef: null == destinationRef
? _value.destinationRef
: destinationRef // ignore: cast_nullable_to_non_nullable
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(
_value.copyWith(
id:
freezed == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int?,
startDate:
null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
endDate:
null == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime,
destination:
null == destination
? _value.destination
: destination // ignore: cast_nullable_to_non_nullable
as Destination,
activity:
null == activity
? _value.activity
: activity // ignore: cast_nullable_to_non_nullable
as List<Activity>,
id: freezed == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int?,
startDate: null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
endDate: null == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime,
destination: null == destination
? _value.destination
: destination // ignore: cast_nullable_to_non_nullable
as Destination,
activity: null == activity
? _value.activity
: activity // ignore: cast_nullable_to_non_nullable
as List<Activity>,
)
as $Val,
);
@ -168,31 +163,26 @@ class __$$BookingImplCopyWithImpl<$Res>
}) {
return _then(
_$BookingImpl(
id:
freezed == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int?,
startDate:
null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
endDate:
null == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime,
destination:
null == destination
? _value.destination
: destination // ignore: cast_nullable_to_non_nullable
as Destination,
activity:
null == activity
? _value._activity
: activity // ignore: cast_nullable_to_non_nullable
as List<Activity>,
id: freezed == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int?,
startDate: null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
endDate: null == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime,
destination: null == destination
? _value.destination
: destination // ignore: cast_nullable_to_non_nullable
as Destination,
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(
json['destination'] as Map<String, dynamic>,
),
activity:
(json['activity'] as List<dynamic>)
.map((e) => Activity.fromJson(e as Map<String, dynamic>))
.toList(),
activity: (json['activity'] as List<dynamic>)
.map((e) => Activity.fromJson(e as Map<String, dynamic>))
.toList(),
);
Map<String, dynamic> _$$BookingImplToJson(_$BookingImpl instance) =>

@ -75,26 +75,22 @@ class _$BookingSummaryCopyWithImpl<$Res, $Val extends BookingSummary>
}) {
return _then(
_value.copyWith(
id:
null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
name:
null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
startDate:
null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
endDate:
null == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
startDate: null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
endDate: null == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime,
)
as $Val,
);
@ -134,26 +130,22 @@ class __$$BookingSummaryImplCopyWithImpl<$Res>
}) {
return _then(
_$BookingSummaryImpl(
id:
null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
name:
null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
startDate:
null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
endDate:
null == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
startDate: null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
endDate: 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}) {
return _then(
_value.copyWith(
name:
null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
imageUrl:
null == imageUrl
? _value.imageUrl
: imageUrl // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
imageUrl: null == imageUrl
? _value.imageUrl
: imageUrl // ignore: cast_nullable_to_non_nullable
as String,
)
as $Val,
);
@ -106,16 +104,14 @@ class __$$ContinentImplCopyWithImpl<$Res>
$Res call({Object? name = null, Object? imageUrl = null}) {
return _then(
_$ContinentImpl(
name:
null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
imageUrl:
null == imageUrl
? _value.imageUrl
: imageUrl // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
imageUrl: null == imageUrl
? _value.imageUrl
: imageUrl // ignore: cast_nullable_to_non_nullable
as String,
),
);
}

@ -95,41 +95,34 @@ class _$DestinationCopyWithImpl<$Res, $Val extends Destination>
}) {
return _then(
_value.copyWith(
ref:
null == ref
? _value.ref
: ref // ignore: cast_nullable_to_non_nullable
as String,
name:
null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
country:
null == country
? _value.country
: country // ignore: cast_nullable_to_non_nullable
as String,
continent:
null == continent
? _value.continent
: continent // ignore: cast_nullable_to_non_nullable
as String,
knownFor:
null == knownFor
? _value.knownFor
: knownFor // ignore: cast_nullable_to_non_nullable
as String,
tags:
null == tags
? _value.tags
: tags // ignore: cast_nullable_to_non_nullable
as List<String>,
imageUrl:
null == imageUrl
? _value.imageUrl
: imageUrl // ignore: cast_nullable_to_non_nullable
as String,
ref: null == ref
? _value.ref
: ref // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
country: null == country
? _value.country
: country // ignore: cast_nullable_to_non_nullable
as String,
continent: null == continent
? _value.continent
: continent // ignore: cast_nullable_to_non_nullable
as String,
knownFor: null == knownFor
? _value.knownFor
: knownFor // ignore: cast_nullable_to_non_nullable
as String,
tags: null == tags
? _value.tags
: 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,
);
@ -180,41 +173,34 @@ class __$$DestinationImplCopyWithImpl<$Res>
}) {
return _then(
_$DestinationImpl(
ref:
null == ref
? _value.ref
: ref // ignore: cast_nullable_to_non_nullable
as String,
name:
null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
country:
null == country
? _value.country
: country // ignore: cast_nullable_to_non_nullable
as String,
continent:
null == continent
? _value.continent
: continent // ignore: cast_nullable_to_non_nullable
as String,
knownFor:
null == knownFor
? _value.knownFor
: knownFor // ignore: cast_nullable_to_non_nullable
as String,
tags:
null == tags
? _value._tags
: tags // ignore: cast_nullable_to_non_nullable
as List<String>,
imageUrl:
null == imageUrl
? _value.imageUrl
: imageUrl // ignore: cast_nullable_to_non_nullable
as String,
ref: null == ref
? _value.ref
: ref // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
country: null == country
? _value.country
: country // ignore: cast_nullable_to_non_nullable
as String,
continent: null == continent
? _value.continent
: continent // ignore: cast_nullable_to_non_nullable
as String,
knownFor: null == knownFor
? _value.knownFor
: knownFor // ignore: cast_nullable_to_non_nullable
as String,
tags: null == tags
? _value._tags
: 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(
_value.copyWith(
continent:
freezed == continent
? _value.continent
: continent // ignore: cast_nullable_to_non_nullable
as String?,
startDate:
freezed == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime?,
endDate:
freezed == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime?,
guests:
freezed == guests
? _value.guests
: guests // ignore: cast_nullable_to_non_nullable
as int?,
destination:
freezed == destination
? _value.destination
: destination // ignore: cast_nullable_to_non_nullable
as String?,
activities:
null == activities
? _value.activities
: activities // ignore: cast_nullable_to_non_nullable
as List<String>,
continent: freezed == continent
? _value.continent
: continent // ignore: cast_nullable_to_non_nullable
as String?,
startDate: freezed == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime?,
endDate: freezed == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime?,
guests: freezed == guests
? _value.guests
: guests // ignore: cast_nullable_to_non_nullable
as int?,
destination: freezed == destination
? _value.destination
: destination // ignore: cast_nullable_to_non_nullable
as String?,
activities: null == activities
? _value.activities
: activities // ignore: cast_nullable_to_non_nullable
as List<String>,
)
as $Val,
);
@ -168,36 +162,30 @@ class __$$ItineraryConfigImplCopyWithImpl<$Res>
}) {
return _then(
_$ItineraryConfigImpl(
continent:
freezed == continent
? _value.continent
: continent // ignore: cast_nullable_to_non_nullable
as String?,
startDate:
freezed == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime?,
endDate:
freezed == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime?,
guests:
freezed == guests
? _value.guests
: guests // ignore: cast_nullable_to_non_nullable
as int?,
destination:
freezed == destination
? _value.destination
: destination // ignore: cast_nullable_to_non_nullable
as String?,
activities:
null == activities
? _value._activities
: activities // ignore: cast_nullable_to_non_nullable
as List<String>,
continent: freezed == continent
? _value.continent
: continent // ignore: cast_nullable_to_non_nullable
as String?,
startDate: freezed == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime?,
endDate: freezed == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime?,
guests: freezed == guests
? _value.guests
: guests // ignore: cast_nullable_to_non_nullable
as int?,
destination: freezed == destination
? _value.destination
: destination // ignore: cast_nullable_to_non_nullable
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,
) => _$ItineraryConfigImpl(
continent: json['continent'] as String?,
startDate:
json['startDate'] == null
? null
: DateTime.parse(json['startDate'] as String),
endDate:
json['endDate'] == null
? null
: DateTime.parse(json['endDate'] as String),
startDate: json['startDate'] == null
? null
: DateTime.parse(json['startDate'] as String),
endDate: json['endDate'] == null
? null
: DateTime.parse(json['endDate'] as String),
guests: (json['guests'] as num?)?.toInt(),
destination: json['destination'] as String?,
activities:

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

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

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

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

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

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

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

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

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

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

@ -93,31 +93,29 @@ class _HomeScreenState extends State<HomeScreen> {
),
SliverList.builder(
itemCount: widget.viewModel.bookings.length,
itemBuilder:
(_, index) => _Booking(
key: ValueKey(widget.viewModel.bookings[index].id),
booking: widget.viewModel.bookings[index],
onTap:
() => context.push(
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;
}
},
itemBuilder: (_, index) => _Booking(
key: ValueKey(widget.viewModel.bookings[index].id),
booking: widget.viewModel.bookings[index],
onTap: () => context.push(
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;
}
},
),
),
],
);

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

Loading…
Cancel
Save