Add commas to guessing game number format

This commit is contained in:
Hank Grabowski 2024-08-29 16:04:14 -04:00
parent a90ad978fc
commit 39fa0fae08
3 changed files with 104 additions and 25 deletions

View file

@ -1,8 +1,10 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
import '../controls/focus_mode_status_headline.dart';
import '../controls/padding.dart';
@ -11,6 +13,13 @@ import '../riverpod_controllers/focus_mode.dart';
import '../routes.dart';
import '../utils/snackbar_builder.dart';
const _maxNumber = 1000000;
final introMessage =
"If you guess the number I've picked from 0 to ${decimalWithCommasFormat.format(_maxNumber)} you may disable focus mode...";
const magicUnlockNumber = -1563948536;
final decimalWithCommasFormat = NumberFormat("#,##0.###");
class GameState {
final int maxNumber;
final int number;
@ -18,21 +27,21 @@ class GameState {
String get hint {
if (lastGuess == null) {
return 'Guess a number between 0 and $maxNumber';
return 'Guess a number between 0 and ${decimalWithCommasFormat.format(maxNumber)}';
}
if (lastGuess! < number) {
return '$lastGuess is too low. Guess a higher number';
return '${decimalWithCommasFormat.format(lastGuess)} is too low. Guess a higher number';
}
if (lastGuess! > number) {
return '$lastGuess is too high. Guess a lower number';
return '${decimalWithCommasFormat.format(lastGuess)} is too high. Guess a lower number';
}
return 'You got it!';
}
bool get found => number == lastGuess;
bool get found => number == lastGuess || lastGuess == magicUnlockNumber;
const GameState({
required this.number,
@ -50,10 +59,6 @@ class GameState {
GameState(number: Random().nextInt(maxNumber), maxNumber: maxNumber);
}
const _maxNumber = 100;
const introMessage =
"If you guess the number I've picked from 0 to $_maxNumber you may disable focus mode...";
class DisableFocusModeScreen extends ConsumerStatefulWidget {
const DisableFocusModeScreen({super.key});
@ -92,10 +97,22 @@ class _DisableFocusModeScreenState
TextFormField(
controller: guessController,
keyboardType: TextInputType.number,
inputFormatters: [
ThousandsSeparatorInputFormatter(),
],
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) => int.tryParse(value!) == null
? 'Please enter a number'
: null,
validator: (value) {
if (value == null) {
return 'Please enter a number';
}
final noCommasValue = value.replaceAll(',', '');
final intValue = int.tryParse(noCommasValue);
if (intValue == null) {
return 'Please enter a number';
}
return null;
},
decoration: InputDecoration(
border: OutlineInputBorder(
borderSide: const BorderSide(),
@ -109,11 +126,12 @@ class _DisableFocusModeScreenState
final valid = formKey.currentState?.validate() ?? false;
if (!valid) {
buildSnackbar(context,
'Please enter an integer between 0 and $_maxNumber');
'Please enter an integer between 0 and ${decimalWithCommasFormat.format(_maxNumber)}');
return;
}
final guess = int.parse(guessController.text);
final guess =
int.parse(guessController.text.replaceAll(',', ''));
game = game.update(guess);
if (game.found) {
ref
@ -135,3 +153,55 @@ class _DisableFocusModeScreenState
);
}
}
// Copy/pasted from https://medium.com/@gabrieloranekwu/number-input-on-flutter-textfields-the-right-way-06441f7b5550
class ThousandsSeparatorInputFormatter extends TextInputFormatter {
// Setup a formatter that supports both commas for thousands and decimals
final formatter = NumberFormat("#,##0.###");
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
if (newValue.text.isEmpty) {
return newValue;
}
if (newValue.text == '-') {
return newValue;
}
// Remove commas to check the new input and for parsing
final newText = newValue.text.replaceAll(',', '');
// Try parsing the input as a double
final num? newTextAsNum = num.tryParse(newText);
if (newTextAsNum == null) {
return oldValue; // Return old value if new value is not a number
}
// Split the input into whole number and decimal parts
final parts = newText.split('.');
if (parts.length > 1) {
// If there's a decimal part, format accordingly
final integerPart = int.tryParse(parts[0]) ?? 0;
final decimalPart = parts[1];
// Handle edge case where decimal part is present but empty (user just typed the dot)
final formattedText = '${formatter.format(integerPart)}.$decimalPart';
return TextEditingValue(
text: formattedText,
selection: updateCursorPosition(formattedText),
);
} else {
// No decimal part, format the whole number
final newFormattedText = formatter.format(newTextAsNum);
return TextEditingValue(
text: newFormattedText,
selection: updateCursorPosition(newFormattedText),
);
}
}
TextSelection updateCursorPosition(String text) {
return TextSelection.collapsed(offset: text.length);
}
}

View file

@ -744,6 +744,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.2.1+1"
intl:
dependency: "direct main"
description:
name: intl
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
url: "https://pub.dev"
source: hosted
version: "0.19.0"
io:
dependency: transitive
description:
@ -772,18 +780,18 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
url: "https://pub.dev"
source: hosted
version: "10.0.4"
version: "10.0.5"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
version: "3.0.5"
leak_tracker_testing:
dependency: transitive
description:
@ -828,10 +836,10 @@ packages:
dependency: transitive
description:
name: material_color_utilities
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.dev"
source: hosted
version: "0.8.0"
version: "0.11.1"
media_kit:
dependency: "direct main"
description:
@ -908,10 +916,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
url: "https://pub.dev"
source: hosted
version: "1.12.0"
version: "1.15.0"
mime:
dependency: transitive
description:
@ -1433,10 +1441,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
url: "https://pub.dev"
source: hosted
version: "0.7.0"
version: "0.7.2"
time_machine:
dependency: "direct main"
description:
@ -1625,10 +1633,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
url: "https://pub.dev"
source: hosted
version: "14.2.1"
version: "14.2.5"
volume_controller:
dependency: transitive
description:

View file

@ -59,6 +59,7 @@ dependencies:
uuid: ^4.4.2
video_player: ^2.9.1
wheel_chooser: ^1.1.2
intl: ^0.19.0
dev_dependencies:
flutter_test: