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.
188 lines
5.3 KiB
188 lines
5.3 KiB
1 year ago
|
// Copyright 2023 The Flutter Authors. 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:extra_alignments/extra_alignments.dart';
|
||
|
import 'package:flutter/material.dart';
|
||
|
import 'package:focusable_control_builder/focusable_control_builder.dart';
|
||
|
import 'package:gap/gap.dart';
|
||
|
|
||
|
import '../assets.dart';
|
||
|
import '../common/ui_scaler.dart';
|
||
|
import '../styles.dart';
|
||
|
|
||
|
class TitleScreenUi extends StatelessWidget {
|
||
|
const TitleScreenUi({
|
||
|
super.key,
|
||
|
required this.difficulty,
|
||
|
required this.onDifficultyPressed,
|
||
|
required this.onDifficultyFocused,
|
||
|
});
|
||
|
|
||
|
final int difficulty;
|
||
|
final void Function(int difficulty) onDifficultyPressed;
|
||
|
final void Function(int? difficulty) onDifficultyFocused;
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return Padding(
|
||
|
padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 50),
|
||
|
child: Stack(
|
||
|
children: [
|
||
|
/// Title Text
|
||
|
const TopLeft(
|
||
|
child: UiScaler(
|
||
|
alignment: Alignment.topLeft,
|
||
|
child: _TitleText(),
|
||
|
),
|
||
|
),
|
||
|
|
||
|
/// Difficulty Btns
|
||
|
BottomLeft(
|
||
|
child: UiScaler(
|
||
|
alignment: Alignment.bottomLeft,
|
||
|
child: _DifficultyBtns(
|
||
|
difficulty: difficulty,
|
||
|
onDifficultyPressed: onDifficultyPressed,
|
||
|
onDifficultyFocused: onDifficultyFocused,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class _TitleText extends StatelessWidget {
|
||
|
const _TitleText();
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return Column(
|
||
|
mainAxisSize: MainAxisSize.min,
|
||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
children: [
|
||
|
const Gap(20),
|
||
|
Row(
|
||
|
mainAxisSize: MainAxisSize.min,
|
||
|
children: [
|
||
|
Transform.translate(
|
||
|
offset: Offset(-(TextStyles.h1.letterSpacing! * .5), 0),
|
||
|
child: Text('OUTPOST', style: TextStyles.h1),
|
||
|
),
|
||
|
Image.asset(AssetPaths.titleSelectedLeft, height: 65),
|
||
|
Text('57', style: TextStyles.h2),
|
||
|
Image.asset(AssetPaths.titleSelectedRight, height: 65),
|
||
|
],
|
||
|
),
|
||
|
Text('INTO THE UNKNOWN', style: TextStyles.h3),
|
||
|
],
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class _DifficultyBtns extends StatelessWidget {
|
||
|
const _DifficultyBtns({
|
||
|
required this.difficulty,
|
||
|
required this.onDifficultyPressed,
|
||
|
required this.onDifficultyFocused,
|
||
|
});
|
||
|
|
||
|
final int difficulty;
|
||
|
final void Function(int difficulty) onDifficultyPressed;
|
||
|
final void Function(int? difficulty) onDifficultyFocused;
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return Column(
|
||
|
mainAxisSize: MainAxisSize.min,
|
||
|
children: [
|
||
|
_DifficultyBtn(
|
||
|
label: 'Casual',
|
||
|
selected: difficulty == 0,
|
||
|
onPressed: () => onDifficultyPressed(0),
|
||
|
onHover: (over) => onDifficultyFocused(over ? 0 : null),
|
||
|
),
|
||
|
_DifficultyBtn(
|
||
|
label: 'Normal',
|
||
|
selected: difficulty == 1,
|
||
|
onPressed: () => onDifficultyPressed(1),
|
||
|
onHover: (over) => onDifficultyFocused(over ? 1 : null),
|
||
|
),
|
||
|
_DifficultyBtn(
|
||
|
label: 'Hardcore',
|
||
|
selected: difficulty == 2,
|
||
|
onPressed: () => onDifficultyPressed(2),
|
||
|
onHover: (over) => onDifficultyFocused(over ? 2 : null),
|
||
|
),
|
||
|
const Gap(20),
|
||
|
],
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class _DifficultyBtn extends StatelessWidget {
|
||
|
const _DifficultyBtn({
|
||
|
required this.selected,
|
||
|
required this.onPressed,
|
||
|
required this.onHover,
|
||
|
required this.label,
|
||
|
});
|
||
|
final String label;
|
||
|
final bool selected;
|
||
|
final VoidCallback onPressed;
|
||
|
final void Function(bool hasFocus) onHover;
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return FocusableControlBuilder(
|
||
|
onPressed: onPressed,
|
||
|
onHoverChanged: (_, state) => onHover.call(state.isHovered),
|
||
|
builder: (_, state) {
|
||
|
return Padding(
|
||
|
padding: const EdgeInsets.all(8.0),
|
||
|
child: SizedBox(
|
||
|
width: 250,
|
||
|
height: 60,
|
||
|
child: Stack(
|
||
|
children: [
|
||
|
// Bg with fill and outline
|
||
|
Container(
|
||
|
decoration: BoxDecoration(
|
||
|
color: const Color(0xFF00D1FF).withOpacity(.1),
|
||
|
border: Border.all(color: Colors.white, width: 5),
|
||
|
),
|
||
|
),
|
||
|
|
||
|
if (state.isHovered || state.isFocused) ...[
|
||
|
Container(
|
||
|
decoration: BoxDecoration(
|
||
|
color: const Color(0xFF00D1FF).withOpacity(.1),
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
|
||
|
/// cross-hairs (selected state)
|
||
|
if (selected) ...[
|
||
|
CenterLeft(
|
||
|
child: Image.asset(AssetPaths.titleSelectedLeft),
|
||
|
),
|
||
|
CenterRight(
|
||
|
child: Image.asset(AssetPaths.titleSelectedRight),
|
||
|
),
|
||
|
],
|
||
|
|
||
|
/// Label
|
||
|
Center(
|
||
|
child: Text(label.toUpperCase(), style: TextStyles.btn),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
);
|
||
|
},
|
||
|
);
|
||
|
}
|
||
|
}
|