mirror of
https://github.com/flutter/samples.git
synced 2026-05-07 15:36:43 +00:00
181 lines
5.7 KiB
Dart
181 lines
5.7 KiB
Dart
// Copyright 2022, the Flutter project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
import 'dart:async';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:logging/logging.dart' hide Level;
|
|
import 'package:provider/provider.dart';
|
|
|
|
import '../ads/ads_controller.dart';
|
|
import '../audio/audio_controller.dart';
|
|
import '../audio/sounds.dart';
|
|
import '../game_internals/level_state.dart';
|
|
import '../games_services/games_services.dart';
|
|
import '../games_services/score.dart';
|
|
import '../in_app_purchase/in_app_purchase.dart';
|
|
import '../level_selection/levels.dart';
|
|
import '../player_progress/player_progress.dart';
|
|
import '../style/confetti.dart';
|
|
import '../style/palette.dart';
|
|
|
|
class PlaySessionScreen extends StatefulWidget {
|
|
final GameLevel level;
|
|
|
|
const PlaySessionScreen(this.level, {super.key});
|
|
|
|
@override
|
|
State<PlaySessionScreen> createState() => _PlaySessionScreenState();
|
|
}
|
|
|
|
class _PlaySessionScreenState extends State<PlaySessionScreen> {
|
|
static final _log = Logger('PlaySessionScreen');
|
|
|
|
static const _celebrationDuration = Duration(milliseconds: 2000);
|
|
|
|
static const _preCelebrationDuration = Duration(milliseconds: 500);
|
|
|
|
bool _duringCelebration = false;
|
|
|
|
late DateTime _startOfPlay;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final palette = context.watch<Palette>();
|
|
|
|
return MultiProvider(
|
|
providers: [
|
|
ChangeNotifierProvider(
|
|
create: (context) => LevelState(
|
|
goal: widget.level.difficulty,
|
|
onWin: _playerWon,
|
|
),
|
|
),
|
|
],
|
|
child: IgnorePointer(
|
|
ignoring: _duringCelebration,
|
|
child: Scaffold(
|
|
backgroundColor: palette.backgroundPlaySession,
|
|
body: Stack(
|
|
children: [
|
|
Center(
|
|
// This is the entirety of the "game".
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Align(
|
|
alignment: Alignment.centerRight,
|
|
child: InkResponse(
|
|
onTap: () => GoRouter.of(context).push('/settings'),
|
|
child: Image.asset(
|
|
'assets/images/settings.png',
|
|
semanticLabel: 'Settings',
|
|
),
|
|
),
|
|
),
|
|
const Spacer(),
|
|
Text('Drag the slider to ${widget.level.difficulty}%'
|
|
' or above!'),
|
|
Consumer<LevelState>(
|
|
builder: (context, levelState, child) => Slider(
|
|
label: 'Level Progress',
|
|
autofocus: true,
|
|
value: levelState.progress / 100,
|
|
onChanged: (value) =>
|
|
levelState.setProgress((value * 100).round()),
|
|
onChangeEnd: (value) => levelState.evaluate(),
|
|
),
|
|
),
|
|
const Spacer(),
|
|
Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: SizedBox(
|
|
width: double.infinity,
|
|
child: FilledButton(
|
|
onPressed: () => GoRouter.of(context).go('/play'),
|
|
child: const Text('Back'),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
SizedBox.expand(
|
|
child: Visibility(
|
|
visible: _duringCelebration,
|
|
child: IgnorePointer(
|
|
child: Confetti(
|
|
isStopped: !_duringCelebration,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
_startOfPlay = DateTime.now();
|
|
|
|
// Preload ad for the win screen.
|
|
final adsRemoved =
|
|
context.read<InAppPurchaseController?>()?.adRemoval.active ?? false;
|
|
if (!adsRemoved) {
|
|
final adsController = context.read<AdsController?>();
|
|
adsController?.preloadAd();
|
|
}
|
|
}
|
|
|
|
Future<void> _playerWon() async {
|
|
_log.info('Level ${widget.level.number} won');
|
|
|
|
final score = Score(
|
|
widget.level.number,
|
|
widget.level.difficulty,
|
|
DateTime.now().difference(_startOfPlay),
|
|
);
|
|
|
|
final playerProgress = context.read<PlayerProgress>();
|
|
playerProgress.setLevelReached(widget.level.number);
|
|
|
|
// Let the player see the game just after winning for a bit.
|
|
await Future<void>.delayed(_preCelebrationDuration);
|
|
if (!mounted) return;
|
|
|
|
setState(() {
|
|
_duringCelebration = true;
|
|
});
|
|
|
|
final audioController = context.read<AudioController>();
|
|
audioController.playSfx(SfxType.congrats);
|
|
|
|
final gamesServicesController = context.read<GamesServicesController?>();
|
|
if (gamesServicesController != null) {
|
|
// Award achievement.
|
|
if (widget.level.awardsAchievement) {
|
|
await gamesServicesController.awardAchievement(
|
|
android: widget.level.achievementIdAndroid!,
|
|
iOS: widget.level.achievementIdIOS!,
|
|
);
|
|
}
|
|
|
|
// Send score to leaderboard.
|
|
await gamesServicesController.submitLeaderboardScore(score);
|
|
}
|
|
|
|
/// Give the player some time to see the celebration animation.
|
|
await Future<void>.delayed(_celebrationDuration);
|
|
if (!mounted) return;
|
|
|
|
GoRouter.of(context).go('/play/won', extra: {'score': score});
|
|
}
|
|
}
|