mirror of
https://github.com/flutter/samples.git
synced 2025-11-08 13:58:47 +00:00
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 `///`).
This commit is contained in:
@@ -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();
|
||||
_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();
|
||||
_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;
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -61,12 +61,11 @@ class _Title extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return ShaderMask(
|
||||
blendMode: BlendMode.srcIn,
|
||||
shaderCallback:
|
||||
(bounds) => RadialGradient(
|
||||
center: Alignment.bottomLeft,
|
||||
radius: 2,
|
||||
colors: [Colors.purple.shade700, Colors.purple.shade400],
|
||||
).createShader(Rect.fromLTWH(0, 0, bounds.width, bounds.height)),
|
||||
shaderCallback: (bounds) => RadialGradient(
|
||||
center: Alignment.bottomLeft,
|
||||
radius: 2,
|
||||
colors: [Colors.purple.shade700, Colors.purple.shade400],
|
||||
).createShader(Rect.fromLTWH(0, 0, bounds.width, bounds.height)),
|
||||
child: Text(
|
||||
text,
|
||||
style: GoogleFonts.rubik(
|
||||
|
||||
@@ -67,13 +67,12 @@ class ResultsViewModel extends ChangeNotifier {
|
||||
case Ok():
|
||||
{
|
||||
// If the result is Ok, update the list of destinations
|
||||
_destinations =
|
||||
result.value
|
||||
.where(
|
||||
(destination) =>
|
||||
destination.continent == _itineraryConfig!.continent,
|
||||
)
|
||||
.toList();
|
||||
_destinations = result.value
|
||||
.where(
|
||||
(destination) =>
|
||||
destination.continent == _itineraryConfig!.continent,
|
||||
)
|
||||
.toList();
|
||||
_log.fine('Destinations (${_destinations.length}) loaded');
|
||||
}
|
||||
case Error():
|
||||
|
||||
@@ -42,8 +42,9 @@ class ResultCard extends StatelessWidget {
|
||||
spacing: 4.0,
|
||||
runSpacing: 4.0,
|
||||
direction: Axis.horizontal,
|
||||
children:
|
||||
destination.tags.map((e) => TagChip(tag: e)).toList(),
|
||||
children: destination.tags
|
||||
.map((e) => TagChip(tag: e))
|
||||
.toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -67,10 +67,9 @@ class _ResultsScreenState extends State<ResultsScreen> {
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: ErrorIndicator(
|
||||
title:
|
||||
AppLocalization.of(
|
||||
context,
|
||||
).errorWhileLoadingDestinations,
|
||||
title: AppLocalization.of(
|
||||
context,
|
||||
).errorWhileLoadingDestinations,
|
||||
label: AppLocalization.of(context).tryAgain,
|
||||
onPressed: widget.viewModel.search.execute,
|
||||
),
|
||||
|
||||
@@ -75,14 +75,12 @@ class _QuantitySelector extends StatelessWidget {
|
||||
),
|
||||
ListenableBuilder(
|
||||
listenable: viewModel,
|
||||
builder:
|
||||
(context, _) => Text(
|
||||
viewModel.guests.toString(),
|
||||
style:
|
||||
viewModel.guests == 0
|
||||
? Theme.of(context).inputDecorationTheme.hintStyle
|
||||
: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
builder: (context, _) => Text(
|
||||
viewModel.guests.toString(),
|
||||
style: viewModel.guests == 0
|
||||
? Theme.of(context).inputDecorationTheme.hintStyle
|
||||
: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
key: const ValueKey(addGuestsKey),
|
||||
|
||||
@@ -65,10 +65,9 @@ class _SearchFormSubmitState extends State<SearchFormSubmit> {
|
||||
builder: (context, child) {
|
||||
return FilledButton(
|
||||
key: const ValueKey(searchFormSubmitButtonKey),
|
||||
onPressed:
|
||||
widget.viewModel.valid
|
||||
? widget.viewModel.updateItineraryConfig.execute
|
||||
: null,
|
||||
onPressed: widget.viewModel.valid
|
||||
? widget.viewModel.updateItineraryConfig.execute
|
||||
: null,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user