Skip to content

Commit 496b467

Browse files
Compass App: Search form screen (#2353)
Part of the implementation of the Compass App for the Architecture sample. **Merge to `compass-app`** This PR introduces the Search Form Screen, in which users can select a region, a date range and the number of guests. The feature is split in 5 different widgets, each one depending on the `SearchFormViewModel`. The architecture follows the same patterns implemented in the previous PR #2342 TODO later on: - Error handling is yet not implemented, we need to introduce a "logger" and a way to handle error responses. - All repositories return local data, next steps include creating the dart server app. - The search query at the moment only takes the "continent" and not the dates and number of guests, that would be implemented later on as well. ## Demo [Screencast from 2024-07-12 14-30-48.webm](https://github.com/user-attachments/assets/afbcdd4e-617a-49cc-894c-8e082748e572) ## 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 updated/added relevant documentation (doc comments with `///`). - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-devrel channel on [Discord]. <!-- Links --> [Flutter Style Guide]: https://github.com/flutter/flutter/blob/master/docs/contributing/Style-guide-for-Flutter-repo.md [CLA]: https://cla.developers.google.com/ [Discord]: https://github.com/flutter/flutter/blob/master/docs/contributing/Chat.md [Contributors Guide]: https://github.com/flutter/samples/blob/main/CONTRIBUTING.md
1 parent cfedff5 commit 496b467

31 files changed

+1171
-60
lines changed
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1-
import '../data/repositories/destination/destination_repository.dart';
2-
import '../data/repositories/destination/destination_repository_local.dart';
31
import 'package:provider/single_child_widget.dart';
42
import 'package:provider/provider.dart';
53

4+
import '../data/repositories/continent/continent_repository.dart';
5+
import '../data/repositories/continent/continent_repository_local.dart';
6+
import '../data/repositories/destination/destination_repository.dart';
7+
import '../data/repositories/destination/destination_repository_local.dart';
8+
69
/// Configure dependencies as a list of Providers
710
List<SingleChildWidget> get providers {
811
// List of Providers
912
return [
1013
Provider.value(
1114
value: DestinationRepositoryLocal() as DestinationRepository,
1215
),
16+
Provider.value(
17+
value: ContinentRepositoryLocal() as ContinentRepository,
18+
),
1319
];
1420
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class Continent {
2+
/// e.g. 'Europe'
3+
final String name;
4+
5+
/// e.g. 'https://rstr.in/google/tripedia/TmR12QdlVTT'
6+
final String imageUrl;
7+
8+
Continent({
9+
required this.name,
10+
required this.imageUrl,
11+
});
12+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import '../../../utils/result.dart';
2+
import '../../models/continent.dart';
3+
4+
/// Data source with all possible continents.
5+
abstract class ContinentRepository {
6+
/// Get complete list of continents.
7+
Future<Result<List<Continent>>> getContinents();
8+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import '../../../utils/result.dart';
2+
import '../../models/continent.dart';
3+
import 'continent_repository.dart';
4+
5+
/// Local data source with all possible regions.
6+
class ContinentRepositoryLocal implements ContinentRepository {
7+
@override
8+
Future<Result<List<Continent>>> getContinents() {
9+
return Future.value(
10+
Result.ok(
11+
[
12+
Continent(
13+
name: 'Europe',
14+
imageUrl: 'https://rstr.in/google/tripedia/TmR12QdlVTT',
15+
),
16+
Continent(
17+
name: 'Asia',
18+
imageUrl: 'https://rstr.in/google/tripedia/VJ8BXlQg8O1',
19+
),
20+
Continent(
21+
name: 'South America',
22+
imageUrl: 'https://rstr.in/google/tripedia/flm_-o1aI8e',
23+
),
24+
Continent(
25+
name: 'Africa',
26+
imageUrl: 'https://rstr.in/google/tripedia/-nzi8yFOBpF',
27+
),
28+
Continent(
29+
name: 'North America',
30+
imageUrl: 'https://rstr.in/google/tripedia/jlbgFDrSUVE',
31+
),
32+
Continent(
33+
name: 'Oceania',
34+
imageUrl: 'https://rstr.in/google/tripedia/vxyrDE-fZVL',
35+
),
36+
Continent(
37+
name: 'Australia',
38+
imageUrl: 'https://rstr.in/google/tripedia/z6vy6HeRyvZ',
39+
),
40+
],
41+
),
42+
);
43+
}
44+
}

compass_app/app/lib/main.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import 'routing/router.dart';
44
import 'package:flutter/material.dart';
55
import 'package:provider/provider.dart';
66

7+
import 'ui/core/ui/scroll_behavior.dart';
8+
79
void main() {
810
runApp(
911
MultiProvider(
@@ -21,6 +23,7 @@ class MainApp extends StatelessWidget {
2123
@override
2224
Widget build(BuildContext context) {
2325
return MaterialApp.router(
26+
scrollBehavior: AppCustomScrollBehavior(),
2427
theme: AppTheme.lightTheme,
2528
darkTheme: AppTheme.darkTheme,
2629
themeMode: ThemeMode.system,
Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,35 @@
1-
import '../ui/results/widgets/results_screen.dart';
21
import 'package:go_router/go_router.dart';
32
import 'package:provider/provider.dart';
43

54
import '../ui/results/view_models/results_viewmodel.dart';
5+
import '../ui/results/widgets/results_screen.dart';
6+
import '../ui/search_form/view_models/search_form_viewmodel.dart';
7+
import '../ui/search_form/widgets/search_form_screen.dart';
68

79
/// Top go_router entry point
810
final router = GoRouter(
9-
initialLocation: '/results',
11+
initialLocation: '/',
1012
routes: [
1113
GoRoute(
12-
path: '/results',
14+
path: '/',
1315
builder: (context, state) {
14-
final viewModel = ResultsViewModel(
15-
destinationRepository: context.read(),
16-
);
17-
return ResultsScreen(viewModel: viewModel);
16+
final viewModel = SearchFormViewModel(continentRepository: context.read());
17+
return SearchFormScreen(viewModel: viewModel);
1818
},
19+
routes: [
20+
GoRoute(
21+
path: 'results',
22+
builder: (context, state) {
23+
final viewModel = ResultsViewModel(
24+
destinationRepository: context.read(),
25+
);
26+
final parameters = state.uri.queryParameters;
27+
// TODO: Pass the rest of query parameters to the ViewModel
28+
viewModel.search(continent: parameters['continent']);
29+
return ResultsScreen(viewModel: viewModel);
30+
},
31+
),
32+
],
1933
),
2034
],
2135
);

compass_app/app/lib/ui/core/themes/colors.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ class AppColors {
55
static const white1 = Color(0xFFFFF7FA);
66
static const grey1 = Color(0xFFF2F2F2);
77
static const grey2 = Color(0xFF4D4D4D);
8+
static const grey3 = Color(0xFFA4A4A4);
89
static const whiteTransparent =
910
Color(0x4DFFFFFF); // Figma rgba(255, 255, 255, 0.3)
1011
static const blackTransparent =
11-
Color(0x4D000000); // Figma rgba(255, 255, 255, 0.3)
12+
Color(0x4D000000);
1213

1314
static const lightColorScheme = ColorScheme(
1415
brightness: Brightness.light,

compass_app/app/lib/ui/core/themes/theme.dart

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,32 @@ import '../ui/tag_chip.dart';
33
import 'package:flutter/material.dart';
44

55
class AppTheme {
6+
static const _textTheme = TextTheme(
7+
titleMedium: TextStyle(
8+
fontSize: 18,
9+
fontWeight: FontWeight.w500,
10+
),
11+
bodyMedium: TextStyle(
12+
fontSize: 18,
13+
fontWeight: FontWeight.w400,
14+
),
15+
);
16+
17+
static const _inputDecorationTheme = InputDecorationTheme(
18+
hintStyle: TextStyle(
19+
// grey3 works for both light and dark themes
20+
color: AppColors.grey3,
21+
fontSize: 18.0,
22+
fontWeight: FontWeight.w400,
23+
),
24+
);
25+
626
static ThemeData lightTheme = ThemeData(
727
useMaterial3: true,
28+
brightness: Brightness.light,
829
colorScheme: AppColors.lightColorScheme,
30+
textTheme: _textTheme,
31+
inputDecorationTheme: _inputDecorationTheme,
932
extensions: [
1033
TagChipTheme(
1134
chipColor: AppColors.whiteTransparent,
@@ -16,7 +39,10 @@ class AppTheme {
1639

1740
static ThemeData darkTheme = ThemeData(
1841
useMaterial3: true,
42+
brightness: Brightness.dark,
1943
colorScheme: AppColors.darkColorScheme,
44+
textTheme: _textTheme,
45+
inputDecorationTheme: _inputDecorationTheme,
2046
extensions: [
2147
TagChipTheme(
2248
chipColor: AppColors.blackTransparent,
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:go_router/go_router.dart';
3+
4+
import '../themes/colors.dart';
5+
6+
/// Home button to navigate back to the '/' path.
7+
class HomeButton extends StatelessWidget {
8+
const HomeButton({super.key});
9+
10+
@override
11+
Widget build(BuildContext context) {
12+
return SizedBox(
13+
height: 40.0,
14+
width: 40.0,
15+
child: DecoratedBox(
16+
decoration: BoxDecoration(
17+
border: Border.all(color: AppColors.grey1),
18+
borderRadius: BorderRadius.circular(8.0),
19+
),
20+
child: InkWell(
21+
borderRadius: BorderRadius.circular(8.0),
22+
onTap: () {
23+
context.go('/');
24+
},
25+
child: Center(
26+
child: Icon(
27+
size: 24.0,
28+
Icons.home_outlined,
29+
color: Theme.of(context).colorScheme.onSurface,
30+
),
31+
),
32+
),
33+
),
34+
);
35+
}
36+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import 'package:flutter/gestures.dart';
2+
import 'package:flutter/material.dart';
3+
4+
/// Custom scroll behavior to allow dragging with mouse.
5+
/// Necessary to allow dragging with mouse on Continents carousel.
6+
class AppCustomScrollBehavior extends MaterialScrollBehavior {
7+
@override
8+
Set<PointerDeviceKind> get dragDevices => {
9+
PointerDeviceKind.touch,
10+
// Allow to drag with mouse on Regions carousel
11+
PointerDeviceKind.mouse,
12+
};
13+
}

0 commit comments

Comments
 (0)