mirror of https://github.com/flutter/samples.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
4.9 KiB
171 lines
4.9 KiB
// Copyright 2024 The Flutter team. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
|
|
import '../../../routing/routes.dart';
|
|
import '../../core/localization/applocalization.dart';
|
|
import '../../core/themes/dimens.dart';
|
|
import '../../core/ui/error_indicator.dart';
|
|
import '../../core/ui/search_bar.dart';
|
|
import '../view_models/results_viewmodel.dart';
|
|
import 'result_card.dart';
|
|
|
|
class ResultsScreen extends StatefulWidget {
|
|
const ResultsScreen({super.key, required this.viewModel});
|
|
|
|
final ResultsViewModel viewModel;
|
|
|
|
@override
|
|
State<ResultsScreen> createState() => _ResultsScreenState();
|
|
}
|
|
|
|
class _ResultsScreenState extends State<ResultsScreen> {
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
widget.viewModel.updateItineraryConfig.addListener(_onResult);
|
|
}
|
|
|
|
@override
|
|
void didUpdateWidget(covariant ResultsScreen oldWidget) {
|
|
super.didUpdateWidget(oldWidget);
|
|
oldWidget.viewModel.updateItineraryConfig.removeListener(_onResult);
|
|
widget.viewModel.updateItineraryConfig.addListener(_onResult);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
widget.viewModel.updateItineraryConfig.removeListener(_onResult);
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return PopScope(
|
|
canPop: false,
|
|
onPopInvokedWithResult: (didPop, r) {
|
|
if (!didPop) context.go(Routes.search);
|
|
},
|
|
child: Scaffold(
|
|
body: ListenableBuilder(
|
|
listenable: widget.viewModel.search,
|
|
builder: (context, child) {
|
|
if (widget.viewModel.search.completed) {
|
|
return child!;
|
|
}
|
|
return Column(
|
|
children: [
|
|
_AppSearchBar(widget: widget),
|
|
if (widget.viewModel.search.running)
|
|
const Expanded(
|
|
child: Center(child: CircularProgressIndicator()),
|
|
),
|
|
if (widget.viewModel.search.error)
|
|
Expanded(
|
|
child: Center(
|
|
child: ErrorIndicator(
|
|
title: AppLocalization.of(
|
|
context,
|
|
).errorWhileLoadingDestinations,
|
|
label: AppLocalization.of(context).tryAgain,
|
|
onPressed: widget.viewModel.search.execute,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
child: ListenableBuilder(
|
|
listenable: widget.viewModel,
|
|
builder: (context, child) {
|
|
return Padding(
|
|
padding: Dimens.of(context).edgeInsetsScreenHorizontal,
|
|
child: CustomScrollView(
|
|
slivers: [
|
|
SliverToBoxAdapter(child: _AppSearchBar(widget: widget)),
|
|
_Grid(viewModel: widget.viewModel),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
void _onResult() {
|
|
if (widget.viewModel.updateItineraryConfig.completed) {
|
|
widget.viewModel.updateItineraryConfig.clearResult();
|
|
context.go(Routes.activities);
|
|
}
|
|
|
|
if (widget.viewModel.updateItineraryConfig.error) {
|
|
widget.viewModel.updateItineraryConfig.clearResult();
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(AppLocalization.of(context).errorWhileSavingItinerary),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
class _AppSearchBar extends StatelessWidget {
|
|
const _AppSearchBar({required this.widget});
|
|
|
|
final ResultsScreen widget;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return SafeArea(
|
|
top: true,
|
|
bottom: false,
|
|
child: Padding(
|
|
padding: EdgeInsets.only(
|
|
top: Dimens.of(context).paddingScreenVertical,
|
|
bottom: Dimens.mobile.paddingScreenVertical,
|
|
),
|
|
child: AppSearchBar(
|
|
config: widget.viewModel.config,
|
|
onTap: () {
|
|
// Navigate to SearchFormScreen and edit search
|
|
context.pop();
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _Grid extends StatelessWidget {
|
|
const _Grid({required this.viewModel});
|
|
|
|
final ResultsViewModel viewModel;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return SliverGrid(
|
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
|
crossAxisCount: 2,
|
|
crossAxisSpacing: 8.0,
|
|
mainAxisSpacing: 8.0,
|
|
childAspectRatio: 182 / 222,
|
|
),
|
|
delegate: SliverChildBuilderDelegate((context, index) {
|
|
final destination = viewModel.destinations[index];
|
|
return ResultCard(
|
|
key: ValueKey(destination.ref),
|
|
destination: destination,
|
|
onTap: () {
|
|
viewModel.updateItineraryConfig.execute(destination.ref);
|
|
},
|
|
);
|
|
}, childCount: viewModel.destinations.length),
|
|
);
|
|
}
|
|
}
|