mirror of
https://github.com/Linloir/Flutter-Wordle.git
synced 2025-12-17 06:18:12 +08:00
Version 1.0.0311
This commit is contained in:
parent
bb4cec06fa
commit
971de665c0
13866
assets/All.txt
Normal file
13866
assets/All.txt
Normal file
File diff suppressed because it is too large
Load Diff
4615
assets/CET4.txt
Normal file
4615
assets/CET4.txt
Normal file
File diff suppressed because it is too large
Load Diff
8028
assets/CET46.txt
Normal file
8028
assets/CET46.txt
Normal file
File diff suppressed because it is too large
Load Diff
2273
assets/CET6.txt
Normal file
2273
assets/CET6.txt
Normal file
File diff suppressed because it is too large
Load Diff
5987
assets/COCA-Slim.txt
Normal file
5987
assets/COCA-Slim.txt
Normal file
File diff suppressed because it is too large
Load Diff
4343
assets/GRE Slim.txt
Normal file
4343
assets/GRE Slim.txt
Normal file
File diff suppressed because it is too large
Load Diff
3469
assets/HighSchool.txt
Normal file
3469
assets/HighSchool.txt
Normal file
File diff suppressed because it is too large
Load Diff
1520
assets/TOEFL Slim.txt
Normal file
1520
assets/TOEFL Slim.txt
Normal file
File diff suppressed because it is too large
Load Diff
4516
assets/TOEFL.txt
Normal file
4516
assets/TOEFL.txt
Normal file
File diff suppressed because it is too large
Load Diff
79339
assets/ospd.txt
79339
assets/ospd.txt
File diff suppressed because it is too large
Load Diff
25321
assets/popular.txt
25321
assets/popular.txt
File diff suppressed because it is too large
Load Diff
235887
assets/unixWords.txt
235887
assets/unixWords.txt
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-03-05 20:56:05
|
* @Date : 2022-03-05 20:56:05
|
||||||
* @LastEditTime : 2022-03-08 21:47:38
|
* @LastEditTime : 2022-03-11 14:54:04
|
||||||
* @Description : The display widget of the wordle game
|
* @Description : The display widget of the wordle game
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -12,7 +12,10 @@ import './event_bus.dart';
|
|||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
class WordleDisplayWidget extends StatefulWidget {
|
class WordleDisplayWidget extends StatefulWidget {
|
||||||
const WordleDisplayWidget({Key? key}) : super(key: key);
|
const WordleDisplayWidget({Key? key, required this.wordLen, required this.maxChances}) : super(key: key);
|
||||||
|
|
||||||
|
final int wordLen;
|
||||||
|
final int maxChances;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<WordleDisplayWidget> createState() => _WordleDisplayWidgetState();
|
State<WordleDisplayWidget> createState() => _WordleDisplayWidgetState();
|
||||||
@ -28,7 +31,7 @@ class _WordleDisplayWidgetState extends State<WordleDisplayWidget> with TickerPr
|
|||||||
void _validationAnimation(List<int> validation) async {
|
void _validationAnimation(List<int> validation) async {
|
||||||
onAnimation = true;
|
onAnimation = true;
|
||||||
bool result = true;
|
bool result = true;
|
||||||
for(int i = 0; i < 5 && onAnimation; i++) {
|
for(int i = 0; i < widget.wordLen && onAnimation; i++) {
|
||||||
setState((){
|
setState((){
|
||||||
inputs[r][i]["State"] = validation[i];
|
inputs[r][i]["State"] = validation[i];
|
||||||
});
|
});
|
||||||
@ -44,7 +47,7 @@ class _WordleDisplayWidgetState extends State<WordleDisplayWidget> with TickerPr
|
|||||||
onAnimation = false;
|
onAnimation = false;
|
||||||
r++;
|
r++;
|
||||||
c = 0;
|
c = 0;
|
||||||
if(r == 6 || result == true) {
|
if(r == widget.maxChances || result == true) {
|
||||||
mainBus.emit(event: "ValidationEnds", args: result);
|
mainBus.emit(event: "ValidationEnds", args: result);
|
||||||
acceptInput = false;
|
acceptInput = false;
|
||||||
}
|
}
|
||||||
@ -61,8 +64,8 @@ class _WordleDisplayWidgetState extends State<WordleDisplayWidget> with TickerPr
|
|||||||
c = 0;
|
c = 0;
|
||||||
onAnimation = false;
|
onAnimation = false;
|
||||||
acceptInput = true;
|
acceptInput = true;
|
||||||
for(int i = 0; i < 6; i++) {
|
for(int i = 0; i < widget.maxChances; i++) {
|
||||||
for(int j = 0; j < 5; j++) {
|
for(int j = 0; j < widget.wordLen; j++) {
|
||||||
inputs[i][j]["Letter"] = "";
|
inputs[i][j]["Letter"] = "";
|
||||||
inputs[i][j]["State"] = 0;
|
inputs[i][j]["State"] = 0;
|
||||||
}
|
}
|
||||||
@ -74,9 +77,9 @@ class _WordleDisplayWidgetState extends State<WordleDisplayWidget> with TickerPr
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
inputs = [
|
inputs = [
|
||||||
for(int i = 0; i < 6; i++)
|
for(int i = 0; i < widget.maxChances; i++)
|
||||||
[
|
[
|
||||||
for(int j = 0; j < 5; j++)
|
for(int j = 0; j < widget.wordLen; j++)
|
||||||
{
|
{
|
||||||
"Letter": "",
|
"Letter": "",
|
||||||
"State": 0,
|
"State": 0,
|
||||||
@ -111,17 +114,17 @@ class _WordleDisplayWidgetState extends State<WordleDisplayWidget> with TickerPr
|
|||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 5 / 6,
|
aspectRatio: widget.wordLen / widget.maxChances,
|
||||||
child: Column(
|
child: Column(
|
||||||
//Column(
|
//Column(
|
||||||
children: [
|
children: [
|
||||||
for(int i = 0; i < 6; i++)
|
for(int i = 0; i < widget.maxChances; i++)
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 1,
|
flex: 1,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
for (int j = 0; j < 5; j++)
|
for (int j = 0; j < widget.wordLen; j++)
|
||||||
AnimatedBuilder(
|
AnimatedBuilder(
|
||||||
animation: inputs[i][j]["InputAnimationController"],
|
animation: inputs[i][j]["InputAnimationController"],
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
@ -206,7 +209,7 @@ class _WordleDisplayWidgetState extends State<WordleDisplayWidget> with TickerPr
|
|||||||
),
|
),
|
||||||
onNotification: (noti) {
|
onNotification: (noti) {
|
||||||
if(noti.type == InputType.singleCharacter) {
|
if(noti.type == InputType.singleCharacter) {
|
||||||
if(r < 6 && c < 5 && !onAnimation && acceptInput) {
|
if(r < widget.maxChances && c < widget.wordLen && !onAnimation && acceptInput) {
|
||||||
setState((){
|
setState((){
|
||||||
inputs[r][c]["Letter"] = noti.msg;
|
inputs[r][c]["Letter"] = noti.msg;
|
||||||
inputs[r][c]["State"] = 3;
|
inputs[r][c]["State"] = 3;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-03-05 20:41:41
|
* @Date : 2022-03-05 20:41:41
|
||||||
* @LastEditTime : 2022-03-09 13:10:14
|
* @LastEditTime : 2022-03-11 14:54:15
|
||||||
* @Description : Offline page
|
* @Description : Offline page
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -14,14 +14,19 @@ import 'package:wordle/display_pannel.dart';
|
|||||||
import 'package:wordle/instruction_pannel.dart';
|
import 'package:wordle/instruction_pannel.dart';
|
||||||
import 'package:wordle/popper_generator.dart';
|
import 'package:wordle/popper_generator.dart';
|
||||||
|
|
||||||
class OfflinePage extends StatefulWidget {
|
class GamePage extends StatefulWidget {
|
||||||
const OfflinePage({Key? key}) : super(key: key);
|
const GamePage({Key? key, required this.database, required this.wordLen, required this.maxChances, required this.gameMode}) : super(key: key);
|
||||||
|
|
||||||
|
final Map<String, List<String>> database;
|
||||||
|
final int wordLen;
|
||||||
|
final int maxChances;
|
||||||
|
final int gameMode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<OfflinePage> createState() => _OfflinePageState();
|
State<GamePage> createState() => _GamePageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _OfflinePageState extends State<OfflinePage> with TickerProviderStateMixin{
|
class _GamePageState extends State<GamePage> with TickerProviderStateMixin{
|
||||||
late AnimationController _controller;
|
late AnimationController _controller;
|
||||||
|
|
||||||
void _onGameEnd(dynamic args) {
|
void _onGameEnd(dynamic args) {
|
||||||
@ -68,7 +73,7 @@ class _OfflinePageState extends State<OfflinePage> with TickerProviderStateMixin
|
|||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 20.0,
|
fontSize: 20.0,
|
||||||
),
|
),
|
||||||
title: const Text('WORDLE OFFLINE'),
|
title: const Text('WORDLE'),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
//iconTheme: const IconThemeData(color: Colors.black),
|
//iconTheme: const IconThemeData(color: Colors.black),
|
||||||
actions: [
|
actions: [
|
||||||
@ -118,8 +123,12 @@ class _OfflinePageState extends State<OfflinePage> with TickerProviderStateMixin
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: Container(
|
body: Container(
|
||||||
child: const ValidationProvider(
|
child: ValidationProvider(
|
||||||
child: WordleDisplayWidget(),
|
database: widget.database,
|
||||||
|
wordLen: widget.wordLen,
|
||||||
|
maxChances: widget.maxChances,
|
||||||
|
gameMode: widget.gameMode,
|
||||||
|
child: WordleDisplayWidget(wordLen: widget.wordLen, maxChances: widget.maxChances),
|
||||||
),
|
),
|
||||||
color: Theme.of(context).brightness == Brightness.dark ? Colors.grey[850] : Colors.white,
|
color: Theme.of(context).brightness == Brightness.dark ? Colors.grey[850] : Colors.white,
|
||||||
),
|
),
|
||||||
@ -1,79 +1,58 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-03-06 15:03:57
|
* @Date : 2022-03-06 15:03:57
|
||||||
* @LastEditTime : 2022-03-07 15:55:48
|
* @LastEditTime : 2022-03-11 13:56:33
|
||||||
* @Description : Word generator
|
* @Description : Word generator
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'package:flutter/services.dart' show rootBundle;
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
|
||||||
abstract class Words {
|
Future<Set<String>> generateDictionary() async {
|
||||||
static Set<String> dataBase = <String>{};
|
// Directory documentRoot = await getApplicationDocumentsDirectory();
|
||||||
static Set<String> dictionary = <String>{};
|
// String dicPath = documentRoot.path + Platform.pathSeparator + dicName + ".txt";
|
||||||
static int _length = 5;
|
// File dicFile = File(dicPath);
|
||||||
static String _cache = "";
|
String dicContents = await rootBundle.loadString("assets/All.txt");
|
||||||
//static Map<String, String> explainations = {};
|
Set<String> database = {};
|
||||||
|
LineSplitter.split(dicContents).forEach((line) {
|
||||||
static Future<bool> importWordsDatabase({int length = 5}) async {
|
database.add(line.toUpperCase());
|
||||||
//explainations.clear();
|
});
|
||||||
if(length != _length || dataBase.isEmpty || dictionary.isEmpty){
|
return database;
|
||||||
_length = length;
|
|
||||||
dataBase.clear();
|
|
||||||
_cache = "";
|
|
||||||
try {
|
|
||||||
var data = await rootBundle.loadString('assets/popular.txt');
|
|
||||||
LineSplitter.split(data).forEach((line) {
|
|
||||||
if(line.length == length && dataBase.lookup(line.substring(0,length - 1)) == null && dataBase.lookup(line.substring(0, length - 2)) == null) {
|
|
||||||
dataBase.add(line.toLowerCase());
|
|
||||||
}
|
|
||||||
_cache = line;
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
throw "Failed loading question database";
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
var data = await rootBundle.loadString('assets/unixWords.txt');
|
|
||||||
LineSplitter.split(data).forEach((line) {
|
|
||||||
if(line.length == length && dictionary.lookup(line.substring(0,length - 1)) == null && dictionary.lookup(line.substring(0, length - 2)) == null) {
|
|
||||||
dictionary.add(line.toLowerCase());
|
|
||||||
}
|
|
||||||
_cache = line;
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
throw "Failed loading validation database";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(dataBase.isEmpty) {
|
|
||||||
throw "Empty question database";
|
|
||||||
}
|
|
||||||
else if(dictionary.isEmpty) {
|
|
||||||
throw "Empty validation database";
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<String> generateWord() async {
|
|
||||||
int bound = dataBase.length;
|
|
||||||
if(bound == 0) {
|
|
||||||
try{
|
|
||||||
await Words.importWordsDatabase(length: _length);
|
|
||||||
} catch (e) {
|
|
||||||
return 'crash';
|
|
||||||
}
|
|
||||||
bound = dataBase.length;
|
|
||||||
}
|
|
||||||
var rng = Random();
|
|
||||||
String res = dataBase.elementAt(rng.nextInt(bound));
|
|
||||||
if(dictionary.lookup(res) == null) {
|
|
||||||
dictionary.add(res);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isWordValidate(String word) {
|
|
||||||
return dictionary.lookup(word.toLowerCase()) != null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Map<String, List<String>>> generateQuestionSet({required String dicName, required int wordLen}) async {
|
||||||
|
// Directory documentRoot = await getApplicationDocumentsDirectory();
|
||||||
|
// String dicPath = documentRoot.path + Platform.pathSeparator + dicName + ".txt";
|
||||||
|
// File dicFile = File(dicPath);
|
||||||
|
String dicContents = await rootBundle.loadString("assets/" + dicName + ".txt");
|
||||||
|
Map<String, List<String>> database = {};
|
||||||
|
LineSplitter.split(dicContents).forEach((line) {
|
||||||
|
var vowelStart = line.indexOf('[');
|
||||||
|
var vowelEnd = line.indexOf(']');
|
||||||
|
var word = "";
|
||||||
|
var vowel = "";
|
||||||
|
var explain = "";
|
||||||
|
word = line.substring(0, vowelStart == -1 ? null : vowelStart).trim().toUpperCase();
|
||||||
|
if(vowelStart != -1){
|
||||||
|
vowel = line.substring(vowelStart, vowelEnd + 1).trim();
|
||||||
|
explain = line.substring(vowelEnd + 1).trim();
|
||||||
|
}
|
||||||
|
if(wordLen == -1 || word.length == wordLen) {
|
||||||
|
database[word] = [vowel, explain];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return database;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> fetchOnlineWord() async {
|
||||||
|
await Future.delayed(const Duration(seconds: 2));
|
||||||
|
return "BINGO";
|
||||||
|
}
|
||||||
|
|
||||||
|
String getNextWord(Map<String, List<String>> database) {
|
||||||
|
var rng = Random();
|
||||||
|
return database.keys.elementAt(rng.nextInt(database.length));
|
||||||
|
}
|
||||||
|
|||||||
27
lib/group_shared.dart
Normal file
27
lib/group_shared.dart
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* @Author : Linloir
|
||||||
|
* @Date : 2022-03-10 10:35:47
|
||||||
|
* @LastEditTime : 2022-03-10 13:06:37
|
||||||
|
* @Description :
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SelectNotification extends Notification {
|
||||||
|
const SelectNotification({required this.selection});
|
||||||
|
|
||||||
|
final int selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
class GroupSharedData extends InheritedWidget {
|
||||||
|
const GroupSharedData({Key? key, required child, required this.selected}) : super(key: key, child: child);
|
||||||
|
|
||||||
|
final int selected;
|
||||||
|
|
||||||
|
static GroupSharedData? of(BuildContext context) => context.dependOnInheritedWidgetOfExactType<GroupSharedData>();
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool updateShouldNotify(GroupSharedData oldWidget) {
|
||||||
|
return oldWidget.selected != selected;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-03-05 20:55:53
|
* @Date : 2022-03-05 20:55:53
|
||||||
* @LastEditTime : 2022-03-07 08:07:14
|
* @LastEditTime : 2022-03-11 11:05:10
|
||||||
* @Description : Input pannel class
|
* @Description : Input pannel class
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -30,9 +30,11 @@ class _InputPannelWidgetState extends State<InputPannelWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onAnimationStops(dynamic args) {
|
void _onAnimationStops(dynamic args) {
|
||||||
_cache.forEach((key, value) {
|
setState(() {
|
||||||
setState(() {
|
_cache.forEach((key, value) {
|
||||||
_keyState[key] = value;
|
if(_keyState[key] != 1){
|
||||||
|
_keyState[key] = value;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
70
lib/loading_page.dart
Normal file
70
lib/loading_page.dart
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* @Author : Linloir
|
||||||
|
* @Date : 2022-03-11 09:51:50
|
||||||
|
* @LastEditTime : 2022-03-11 14:54:17
|
||||||
|
* @Description : Game Page
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:wordle/game_page.dart';
|
||||||
|
import 'package:wordle/generator.dart';
|
||||||
|
import 'package:wordle/validation_provider.dart';
|
||||||
|
|
||||||
|
class LoadingPage extends StatefulWidget {
|
||||||
|
const LoadingPage({Key? key, required this.dicName, required this.wordLen, required this.maxChances, required this.gameMode}) : super(key: key);
|
||||||
|
|
||||||
|
final String dicName;
|
||||||
|
final int wordLen;
|
||||||
|
final int maxChances;
|
||||||
|
final int gameMode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<LoadingPage> createState() => _LoadingPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LoadingPageState extends State<LoadingPage> {
|
||||||
|
Future<Map<String, List<String>>> _loadDatabase({required String dicName, required int wordLen, required int gameMode}) async {
|
||||||
|
var dataBase = await generateQuestionSet(dicName: dicName, wordLen: wordLen);
|
||||||
|
if(ValidationProvider.validationDatabase.isEmpty) {
|
||||||
|
ValidationProvider.validationDatabase = await generateDictionary();
|
||||||
|
}
|
||||||
|
return dataBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FutureBuilder(
|
||||||
|
future: _loadDatabase(dicName: widget.dicName, wordLen: widget.wordLen, gameMode: widget.wordLen),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if(snapshot.connectionState == ConnectionState.done) {
|
||||||
|
return GamePage(database: snapshot.data as Map<String, List<String>>, wordLen: widget.wordLen, maxChances: widget.maxChances, gameMode: widget.gameMode);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 50.0,
|
||||||
|
height: 50.0,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: Colors.teal[400],
|
||||||
|
strokeWidth: 4.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 15.0),
|
||||||
|
child: Text('Loading Libraries', style: TextStyle(color: Colors.teal[400], fontWeight: FontWeight.bold, fontSize: 25.0),),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
472
lib/main.dart
472
lib/main.dart
@ -1,15 +1,41 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-03-05 20:21:34
|
* @Date : 2022-03-05 20:21:34
|
||||||
* @LastEditTime : 2022-03-07 14:40:28
|
* @LastEditTime : 2022-03-11 14:56:28
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:wordle/event_bus.dart';
|
import 'package:wordle/event_bus.dart';
|
||||||
import './offline.dart';
|
import 'package:wordle/loading_page.dart';
|
||||||
import './generator.dart';
|
import 'package:wordle/single_selection.dart';
|
||||||
|
import 'package:wordle/selection_group.dart';
|
||||||
|
import 'package:wordle/scroll_behav.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:wordle/instruction_pannel.dart';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
void main() {
|
Future<void> loadSettings() async {
|
||||||
|
Directory documentsDirectory = await getApplicationDocumentsDirectory();
|
||||||
|
String documentsPath = documentsDirectory.path + Platform.pathSeparator;
|
||||||
|
File settings = File(documentsPath + "settings.txt");
|
||||||
|
if(!(await settings.exists())) {
|
||||||
|
var defaultSettings = "5\nCET4\nLight";
|
||||||
|
settings.writeAsString(defaultSettings);
|
||||||
|
}
|
||||||
|
List<String> dicBooks = ["validation.txt", "CET4.txt"];
|
||||||
|
for(String dicName in dicBooks) {
|
||||||
|
if(!(await File(documentsPath + dicName).exists())) {
|
||||||
|
//Copy file
|
||||||
|
ByteData data = await rootBundle.load("assets/CET4.txt");
|
||||||
|
List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
|
||||||
|
await File(documentsPath + dicName).writeAsBytes(bytes, flush: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(){
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,6 +59,7 @@ class _MyAppState extends State<MyApp> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
//loadSettings();
|
||||||
mainBus.onBus(event: "ToggleTheme", onEvent: _onThemeChange);
|
mainBus.onBus(event: "ToggleTheme", onEvent: _onThemeChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,6 +72,7 @@ class _MyAppState extends State<MyApp> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
|
scrollBehavior: MyScrollBehavior(),
|
||||||
title: 'Wordle',
|
title: 'Wordle',
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
primarySwatch: Colors.grey,
|
primarySwatch: Colors.grey,
|
||||||
@ -52,7 +80,6 @@ class _MyAppState extends State<MyApp> {
|
|||||||
),
|
),
|
||||||
routes: {
|
routes: {
|
||||||
"/": (context) => const HomePage(),
|
"/": (context) => const HomePage(),
|
||||||
"/Offline": (context) => const OfflinePage(),
|
|
||||||
},
|
},
|
||||||
initialRoute: "/",
|
initialRoute: "/",
|
||||||
);
|
);
|
||||||
@ -67,137 +94,352 @@ class HomePage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _HomePageState extends State<HomePage> {
|
class _HomePageState extends State<HomePage> {
|
||||||
bool _ignorance = false;
|
//bool _ignorance = false;
|
||||||
|
|
||||||
|
Future<void> readSettings() async {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return FutureBuilder(
|
return FutureBuilder(
|
||||||
future: Words.importWordsDatabase(),
|
future: readSettings(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if(snapshot.connectionState == ConnectionState.done || _ignorance) {
|
if(snapshot.connectionState == ConnectionState.done) {
|
||||||
if(snapshot.hasData || _ignorance){
|
var mode = Theme.of(context).brightness;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: Center(
|
body: Align(
|
||||||
child: Padding(
|
alignment: Alignment.center,
|
||||||
padding: const EdgeInsets.all(50.0),
|
child: Container(
|
||||||
child: AspectRatio(
|
constraints: const BoxConstraints(maxWidth: 960.0),
|
||||||
aspectRatio: 16 / 9,
|
child: Column(
|
||||||
child: OutlinedButton(
|
children: [
|
||||||
child: Padding(
|
Container(
|
||||||
padding: const EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
|
constraints: const BoxConstraints(minHeight: 100.0),
|
||||||
child: Align(
|
child: Row(
|
||||||
alignment: Alignment.centerLeft,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
child: Column(
|
children: [
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
Align(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
alignment: Alignment.bottomLeft,
|
||||||
children: [
|
child: Padding(
|
||||||
Text(
|
padding: const EdgeInsets.only(left: 30.0, bottom: 10.0),
|
||||||
'OFFLINE PLAYGROUND',
|
child: Text('WORDLE', style: TextStyle(fontSize: 30.0, fontWeight: FontWeight.w300, color: mode == Brightness.light ? Colors.grey[850]! : Colors.white)),
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.grey[850]!,
|
|
||||||
fontSize: 20.0,
|
|
||||||
fontWeight: FontWeight.bold
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Padding(
|
|
||||||
// padding: const EdgeInsets.symmetric(vertical: 8.0),
|
|
||||||
// child: Container(
|
|
||||||
// width: 80.0,
|
|
||||||
// height: 8.0,
|
|
||||||
// decoration: BoxDecoration(
|
|
||||||
// color: Colors.grey[800],
|
|
||||||
// borderRadius: BorderRadius.circular(3.5),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
Text(
|
|
||||||
'Play Wordle game offline using local word database',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.grey[500],
|
|
||||||
fontSize: 12.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
const Spacer(),
|
||||||
style: ButtonStyle(
|
Align(
|
||||||
shape: MaterialStateProperty.all<OutlinedBorder?>(RoundedRectangleBorder(borderRadius: BorderRadius.circular(10))),
|
alignment: Alignment.bottomRight,
|
||||||
padding: MaterialStateProperty.all<EdgeInsets?>(const EdgeInsets.all(0)),
|
child: Padding(
|
||||||
),
|
padding: const EdgeInsets.only(right: 30.0, bottom: 10.0),
|
||||||
onPressed: () async {
|
child: Row(
|
||||||
Navigator.of(context).pushNamed("/Offline");
|
children: [
|
||||||
//Future.delayed(const Duration(milliseconds: 10)).then((e) => mainBus.emit(event: "NewGame", args: []));
|
AnimatedSwitcher(
|
||||||
}
|
duration: const Duration(milliseconds: 750),
|
||||||
)
|
reverseDuration: const Duration(milliseconds: 750),
|
||||||
|
switchInCurve: Curves.bounceOut,
|
||||||
|
switchOutCurve: Curves.bounceIn,
|
||||||
|
transitionBuilder: (child, animation) {
|
||||||
|
var rotateAnimation = Tween<double>(begin: 0, end: 2 * pi).animate(animation);
|
||||||
|
var opacAnimation = Tween<double>(begin: 0, end: 1).animate(animation);
|
||||||
|
return AnimatedBuilder(
|
||||||
|
animation: rotateAnimation,
|
||||||
|
builder: (context, child) {
|
||||||
|
return Transform(
|
||||||
|
transform: Matrix4.rotationZ(rotateAnimation.status == AnimationStatus.reverse ? 2 * pi - rotateAnimation.value : rotateAnimation.value),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Opacity(
|
||||||
|
opacity: opacAnimation.value,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: IconButton(
|
||||||
|
key: ValueKey(mode),
|
||||||
|
icon: mode == Brightness.light ? const Icon(Icons.dark_mode_outlined) : const Icon(Icons.dark_mode),
|
||||||
|
onPressed: () => mainBus.emit(event: "ToggleTheme", args: []),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.help_outline_outlined),
|
||||||
|
//color: Colors.black,
|
||||||
|
onPressed: (){
|
||||||
|
showInstructionDialog(context: context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
const Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
|
child: OfflinePage(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
}
|
);
|
||||||
else {
|
|
||||||
return Container(
|
|
||||||
color: Colors.black38,
|
|
||||||
child: AlertDialog(
|
|
||||||
title: const Text('Failure'),
|
|
||||||
content: Text(snapshot.error as String),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
child: const Text('Retry'),
|
|
||||||
onPressed: () => setState(() {}),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
child: const Text('Ignore'),
|
|
||||||
onPressed: () {
|
|
||||||
setState((){
|
|
||||||
_ignorance = true;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return Scaffold(
|
return const Text('');
|
||||||
body: Center(
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OfflinePage extends StatefulWidget {
|
||||||
|
const OfflinePage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<OfflinePage> createState() => _OfflinePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _OfflinePageState extends State<OfflinePage> {
|
||||||
|
int wordLen = 5;
|
||||||
|
int maxChances = 6;
|
||||||
|
int dicBookIndex = 0;
|
||||||
|
var dicBook = [["All", "Full wordlist", "A", Colors.indigo], ["HighSchool", "HighSchool wordlist", "H", Colors.amber],
|
||||||
|
["CET4", "CET4 wordlist", "4", Colors.green[400]], ["CET6", "CET6 wordlist", "6", Colors.teal[400]],
|
||||||
|
["CET4 + 6", "CET4&6 wordlist", "46", Colors.teal[600]], ["TOEFL Slim", "TOEFL without CET4&6", "T", Colors.blue[400]],
|
||||||
|
["TOEFL", "Full TOEFL wordlist", "T", Colors.cyan[400]], ["GRE Slim", "GRE without CET4&6", "G", Colors.pink[200]]];
|
||||||
|
late final List<Widget> dicBookSelections;
|
||||||
|
var wordLenSelectionColors = [Colors.green[300], Colors.green[500], Colors.teal[300], Colors.teal[500], Colors.pink[300]];
|
||||||
|
var chancesSelectionColors = [Colors.deepOrange[600], Colors.orange[400], Colors.cyan, Colors.blue[400], Colors.blue[600], Colors.teal[400], Colors.teal[600], Colors.green[700]];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
dicBookSelections = [
|
||||||
|
for(int i = 0; i < dicBook.length; i++)
|
||||||
|
generateSelectionBox(
|
||||||
|
id: i,
|
||||||
|
width: 190,
|
||||||
|
height: 240,
|
||||||
|
padding: const EdgeInsets.fromLTRB(15.0, 25.0, 15.0, 0.0),
|
||||||
|
color: dicBook[i][3]! as Color,
|
||||||
|
primaryText: dicBook[i][0] as String,
|
||||||
|
primaryTextSize: 20.0,
|
||||||
|
secondaryText: dicBook[i][1] as String,
|
||||||
|
secondaryTextSize: 14.0,
|
||||||
|
decorationText: dicBook[i][2] as String,
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 150.0,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: wordLenSelectionColors[wordLen - 4]!.withOpacity(0.2),
|
||||||
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
),
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
padding: const EdgeInsets.all(10.0),
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
Padding(
|
||||||
height: 60.0,
|
padding: const EdgeInsets.fromLTRB(15.0, 10.0, 0.0, 10.0),
|
||||||
width: 60.0,
|
child: Text(
|
||||||
child: CircularProgressIndicator(
|
'Word Length',
|
||||||
strokeWidth: 6.0,
|
style: TextStyle(
|
||||||
color: Colors.grey[850],
|
fontSize: 22.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: wordLenSelectionColors[wordLen - 4]!,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
SelectionGroupProvider(
|
||||||
padding: const EdgeInsets.all(30.0),
|
defaultSelection: 5,
|
||||||
child: Text('Loading library',
|
onChanged: (sel) => setState(() => wordLen = sel),
|
||||||
style: TextStyle(
|
selections: SingleChildScrollView(
|
||||||
color: Colors.grey[850]!,
|
scrollDirection: Axis.horizontal,
|
||||||
fontSize: 16.0,
|
child: Row(
|
||||||
fontWeight: FontWeight.bold,
|
children: [
|
||||||
|
for(int i = 4; i <= 8; i++)
|
||||||
|
generateSelectionBox(
|
||||||
|
id: i,
|
||||||
|
width: 80.0,
|
||||||
|
height: 80.0,
|
||||||
|
color: wordLenSelectionColors[i - 4]!,
|
||||||
|
primaryText: '$i',
|
||||||
|
primaryTextSize: 25.0,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
Container(
|
||||||
}
|
width: double.infinity,
|
||||||
},
|
height: 150.0,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: chancesSelectionColors[maxChances - 1]!.withOpacity(0.2),
|
||||||
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
),
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
padding: const EdgeInsets.all(10.0),
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(15.0, 10.0, 0.0, 10.0),
|
||||||
|
child: Text(
|
||||||
|
'Max Attempts',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: chancesSelectionColors[maxChances - 1]!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SelectionGroupProvider(
|
||||||
|
defaultSelection: 6,
|
||||||
|
onChanged: (sel) => setState(() => maxChances = sel),
|
||||||
|
selections: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
for(int i = 1; i <= 8; i++)
|
||||||
|
generateSelectionBox(
|
||||||
|
id: i,
|
||||||
|
width: 80.0,
|
||||||
|
height: 80.0,
|
||||||
|
color: chancesSelectionColors[i - 1]!,
|
||||||
|
primaryText: '$i',
|
||||||
|
primaryTextSize: 25.0,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: (dicBook[dicBookIndex][3]! as Color).withOpacity(0.2),
|
||||||
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
),
|
||||||
|
margin: const EdgeInsets.all(10.0),
|
||||||
|
padding: const EdgeInsets.all(15.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(15.0, 10.0, 0.0, 10.0),
|
||||||
|
child: Text(
|
||||||
|
'Word List',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: (dicBook[dicBookIndex][3]! as Color),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SelectionGroupProvider(
|
||||||
|
defaultSelection: 0,
|
||||||
|
onChanged: (sel) => setState(() => dicBookIndex = sel),
|
||||||
|
selections: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: Row(
|
||||||
|
children: dicBookSelections,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 10.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.teal.withOpacity(0.2),
|
||||||
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
),
|
||||||
|
margin: const EdgeInsets.all(10.0),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: (){
|
||||||
|
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
|
||||||
|
return LoadingPage(dicName: dicBook[dicBookIndex][0] as String, wordLen: wordLen, maxChances: maxChances, gameMode: 0);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
child: Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 30.0, vertical: 30.0),
|
||||||
|
child: const Text(
|
||||||
|
'Start Normal',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.teal,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.pink[200]!.withOpacity(0.2),
|
||||||
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
),
|
||||||
|
margin: const EdgeInsets.all(10.0),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: (){},
|
||||||
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
child: Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 30.0, vertical: 30.0),
|
||||||
|
child: Text(
|
||||||
|
'Start Hard',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.pink[400],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Scaffold(
|
|
||||||
// body: Center(
|
|
||||||
// child: ElevatedButton(
|
|
||||||
// child: const Text('Offline'),
|
|
||||||
// onPressed: () => Navigator.of(context).pushNamed("/Offline"),
|
|
||||||
// )
|
|
||||||
// )
|
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
17
lib/scroll_behav.dart
Normal file
17
lib/scroll_behav.dart
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* @Author : Linloir
|
||||||
|
* @Date : 2022-03-10 22:07:28
|
||||||
|
* @LastEditTime : 2022-03-10 22:07:29
|
||||||
|
* @Description : Scroll behavior
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
|
|
||||||
|
class MyScrollBehavior extends ScrollBehavior {
|
||||||
|
@override
|
||||||
|
Set<PointerDeviceKind> get dragDevices => {
|
||||||
|
PointerDeviceKind.touch,
|
||||||
|
PointerDeviceKind.mouse,
|
||||||
|
};
|
||||||
|
}
|
||||||
52
lib/selection_group.dart
Normal file
52
lib/selection_group.dart
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* @Author : Linloir
|
||||||
|
* @Date : 2022-03-09 22:33:34
|
||||||
|
* @LastEditTime : 2022-03-10 13:48:48
|
||||||
|
* @Description :
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import './group_shared.dart';
|
||||||
|
|
||||||
|
class SelectionGroupProvider extends StatefulWidget {
|
||||||
|
const SelectionGroupProvider({
|
||||||
|
Key? key,
|
||||||
|
required this.selections,
|
||||||
|
required this.onChanged,
|
||||||
|
this.defaultSelection = 0,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final Widget selections;
|
||||||
|
final int defaultSelection;
|
||||||
|
final void Function(int) onChanged;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SelectionGroupProvider> createState() => _SelectionGroupProviderState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SelectionGroupProviderState extends State<SelectionGroupProvider> {
|
||||||
|
int selected = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
selected = widget.defaultSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GroupSharedData(
|
||||||
|
child: NotificationListener<SelectNotification>(
|
||||||
|
child: widget.selections,
|
||||||
|
onNotification: (noti) {
|
||||||
|
setState(() {
|
||||||
|
selected = noti.selection;
|
||||||
|
widget.onChanged(selected);
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
selected: selected,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
362
lib/single_selection.dart
Normal file
362
lib/single_selection.dart
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
/*
|
||||||
|
* @Author : Linloir
|
||||||
|
* @Date : 2022-03-09 22:33:41
|
||||||
|
* @LastEditTime : 2022-03-11 14:01:14
|
||||||
|
* @Description : Single Selection Box
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'dart:math';
|
||||||
|
import './group_shared.dart';
|
||||||
|
|
||||||
|
class SingleSelectionBox extends StatefulWidget {
|
||||||
|
const SingleSelectionBox({
|
||||||
|
Key? key,
|
||||||
|
required this.id,
|
||||||
|
required this.unselectedChild,
|
||||||
|
required this.selectedChild,
|
||||||
|
//These two are displayed by animation widget so not include in the decoration parameter
|
||||||
|
required this.selectedBackgroundColor,
|
||||||
|
required this.unselectedBackgroundColor,
|
||||||
|
this.shadowBlurRadius = 8,
|
||||||
|
this.shadowSpreadRadius = 0,
|
||||||
|
this.unselectedShadowColor = Colors.transparent,
|
||||||
|
this.selectedShadowColor = Colors.transparent,
|
||||||
|
this.borderRadius = 0.0,
|
||||||
|
this.borderColor = Colors.transparent,
|
||||||
|
this.borderWidth = 0.0,
|
||||||
|
}):
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
|
final int id;
|
||||||
|
final Widget unselectedChild;
|
||||||
|
final Widget selectedChild;
|
||||||
|
final Color selectedBackgroundColor;
|
||||||
|
final Color unselectedBackgroundColor;
|
||||||
|
final double shadowBlurRadius;
|
||||||
|
final double shadowSpreadRadius;
|
||||||
|
final Color unselectedShadowColor;
|
||||||
|
final Color selectedShadowColor;
|
||||||
|
final double borderRadius;
|
||||||
|
final Color borderColor;
|
||||||
|
final double borderWidth;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SingleSelectionBox> createState() => _SingleSelectionBoxState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SingleSelectionBoxState extends State<SingleSelectionBox> with TickerProviderStateMixin{
|
||||||
|
bool selected = false;
|
||||||
|
|
||||||
|
//Record last pressed shift from center point
|
||||||
|
double pressedX = 0;
|
||||||
|
double pressedY = 0;
|
||||||
|
|
||||||
|
//Grow animation controller
|
||||||
|
late final AnimationController _scaleController;
|
||||||
|
late final AnimationController _opacController;
|
||||||
|
late final AnimationController _shrinkController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_scaleController = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: const Duration(milliseconds: 750),
|
||||||
|
);
|
||||||
|
_opacController = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: const Duration(milliseconds: 750),
|
||||||
|
);
|
||||||
|
_shrinkController = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: const Duration(milliseconds: 500),
|
||||||
|
);
|
||||||
|
if(context.findAncestorWidgetOfExactType<GroupSharedData>()!.selected == widget.id) {
|
||||||
|
_scaleController.value = _scaleController.upperBound;
|
||||||
|
selected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
var newData = context.findAncestorWidgetOfExactType<GroupSharedData>()!;
|
||||||
|
if(newData.selected != widget.id && selected) {
|
||||||
|
_opacController.forward();
|
||||||
|
selected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var data = GroupSharedData.of(context)!;
|
||||||
|
//Use gesture detector to detect the position pressed for animations
|
||||||
|
return LayoutBuilder(
|
||||||
|
builder:(context, constraints) {
|
||||||
|
var decoration = BoxDecoration(
|
||||||
|
boxShadow: [BoxShadow(
|
||||||
|
color: data.selected == widget.id ? widget.selectedShadowColor : widget.unselectedShadowColor,
|
||||||
|
blurRadius: widget.shadowBlurRadius,
|
||||||
|
spreadRadius: widget.shadowSpreadRadius,
|
||||||
|
)],
|
||||||
|
color: widget.unselectedBackgroundColor,
|
||||||
|
border: widget.borderWidth > 0 ? Border.all(
|
||||||
|
color: data.selected == widget.id ? widget.selectedBackgroundColor : widget.borderColor,
|
||||||
|
width: widget.borderWidth
|
||||||
|
) : null,
|
||||||
|
borderRadius: BorderRadius.circular(widget.borderRadius),
|
||||||
|
);
|
||||||
|
return GestureDetector(
|
||||||
|
//Stack best describes the relation between the animation and the container box
|
||||||
|
child: AnimatedBuilder(
|
||||||
|
animation: _shrinkController,
|
||||||
|
builder: (context, child) {
|
||||||
|
var _shrinkAnimation = Tween<double>(begin: 1, end: 0.9).animate(CurvedAnimation(
|
||||||
|
parent: _shrinkController,
|
||||||
|
curve: Curves.elasticOut,
|
||||||
|
reverseCurve: Curves.elasticIn,
|
||||||
|
));
|
||||||
|
return Transform.scale(
|
||||||
|
scale: _shrinkAnimation.value,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
//Container
|
||||||
|
Positioned.fill(
|
||||||
|
child: AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 750),
|
||||||
|
decoration: decoration,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
//Animation ( similar to ink well effect )
|
||||||
|
Positioned.fill(
|
||||||
|
child: AnimatedBuilder(
|
||||||
|
animation: _scaleController,
|
||||||
|
child: AnimatedBuilder(
|
||||||
|
animation: _opacController,
|
||||||
|
builder: (context, child) {
|
||||||
|
num topLeftSquredDis = pow(pressedX, 2) + pow(pressedY, 2);
|
||||||
|
num topRightSquredDis = pow(constraints.maxWidth - pressedX, 2) + pow(pressedY, 2);
|
||||||
|
num botLeftSquredDis = pow(pressedX, 2) + pow(constraints.maxHeight - pressedY, 2);
|
||||||
|
num botRightSquredDis = pow(constraints.maxWidth - pressedX, 2) + pow(constraints.maxHeight - pressedY, 2);
|
||||||
|
num radius = sqrt(max(max(topLeftSquredDis, topRightSquredDis), max(botLeftSquredDis, botRightSquredDis)));
|
||||||
|
var _opacityAnimation = Tween<double>(begin: 1, end: 0).animate(_opacController);
|
||||||
|
return OverflowBox(
|
||||||
|
maxHeight: radius * 2,
|
||||||
|
maxWidth: radius * 2,
|
||||||
|
child: SizedBox(
|
||||||
|
child: Opacity(
|
||||||
|
opacity: _opacityAnimation.value,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: widget.selectedBackgroundColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
width: radius * 2,
|
||||||
|
height: radius * 2,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
builder: (context, child) {
|
||||||
|
var _scaleAnimation = Tween<double>(begin: 0, end: 1).animate(CurvedAnimation(
|
||||||
|
parent: _scaleController,
|
||||||
|
curve: Curves.easeInOutCubic,
|
||||||
|
));
|
||||||
|
return ClipRRect(
|
||||||
|
child: Transform.translate(
|
||||||
|
child: Transform.scale(
|
||||||
|
scale: _scaleAnimation.value,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
offset: Offset(pressedX - constraints.maxWidth / 2, pressedY - constraints.maxHeight / 2),
|
||||||
|
),
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
borderRadius: BorderRadius.circular(widget.borderRadius),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
child: AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 750),
|
||||||
|
child: data.selected == widget.id ? widget.selectedChild : widget.unselectedChild,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTapUp: (detail) {
|
||||||
|
setState(() {
|
||||||
|
selected = true;
|
||||||
|
//Notify the group manager
|
||||||
|
SelectNotification(selection: widget.id).dispatch(context);
|
||||||
|
//Record the coordinate and start the animation
|
||||||
|
pressedX = detail.localPosition.dx;
|
||||||
|
pressedY = detail.localPosition.dy;
|
||||||
|
_opacController.reset();
|
||||||
|
_scaleController.reset();
|
||||||
|
_scaleController.forward();
|
||||||
|
_shrinkController.reverse();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onTapDown: ((detail) {
|
||||||
|
_shrinkController.value = _shrinkController.upperBound;
|
||||||
|
}),
|
||||||
|
onTapCancel: (() {
|
||||||
|
_shrinkController.reverse();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget generateSelectionBox({
|
||||||
|
required int id,
|
||||||
|
required double width,
|
||||||
|
required double height,
|
||||||
|
required Color color,
|
||||||
|
required String primaryText,
|
||||||
|
required double primaryTextSize,
|
||||||
|
String? secondaryText,
|
||||||
|
double? secondaryTextSize,
|
||||||
|
String? decorationText,
|
||||||
|
Alignment alignment = Alignment.center,
|
||||||
|
EdgeInsets padding = EdgeInsets.zero,
|
||||||
|
}) {
|
||||||
|
return Container(
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
padding: const EdgeInsets.all(10.0),
|
||||||
|
child: SingleSelectionBox(
|
||||||
|
id: id,
|
||||||
|
unselectedBackgroundColor: Colors.white.withOpacity(0.6),
|
||||||
|
selectedBackgroundColor: color,
|
||||||
|
//unselectedShadowColor: Colors.grey[400]!,
|
||||||
|
//selectedShadowColor: color,
|
||||||
|
borderRadius: 10.0,
|
||||||
|
borderWidth: 0.0,
|
||||||
|
unselectedChild: Stack(
|
||||||
|
key: const ValueKey(0),
|
||||||
|
children: [
|
||||||
|
Positioned.fill(
|
||||||
|
child: Align(
|
||||||
|
alignment: alignment,
|
||||||
|
child: Padding(
|
||||||
|
padding: padding,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||||
|
child: Text(primaryText, style: TextStyle(
|
||||||
|
color: Colors.grey[850],
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: primaryTextSize,
|
||||||
|
),),
|
||||||
|
),
|
||||||
|
if(secondaryText != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||||
|
child: Text(secondaryText, style: TextStyle(
|
||||||
|
color: Colors.grey[850]!.withOpacity(0.8),
|
||||||
|
fontSize: secondaryTextSize,
|
||||||
|
),),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if(decorationText != null)
|
||||||
|
Positioned.fill(
|
||||||
|
child: ClipRRect(
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.bottomRight,
|
||||||
|
child: Transform.translate(
|
||||||
|
offset: const Offset(15, 35),
|
||||||
|
child: Text(
|
||||||
|
decorationText,
|
||||||
|
style: TextStyle(
|
||||||
|
color: color.withOpacity(0.3),
|
||||||
|
fontSize: 120.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
selectedChild: Stack(
|
||||||
|
key: const ValueKey(1),
|
||||||
|
children: [
|
||||||
|
Positioned.fill(
|
||||||
|
child: Align(
|
||||||
|
alignment: alignment,
|
||||||
|
child: Padding(
|
||||||
|
padding: padding,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||||
|
child: Text(primaryText, style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: primaryTextSize,
|
||||||
|
),),
|
||||||
|
),
|
||||||
|
if(secondaryText != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||||
|
child: Text(secondaryText, style: TextStyle(
|
||||||
|
color: Colors.white.withOpacity(0.8),
|
||||||
|
fontSize: secondaryTextSize,
|
||||||
|
),),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if(decorationText != null)
|
||||||
|
Positioned.fill(
|
||||||
|
child: ClipRRect(
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.bottomRight,
|
||||||
|
child: Transform.translate(
|
||||||
|
offset: const Offset(15, 35),
|
||||||
|
child: Text(
|
||||||
|
decorationText,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white.withOpacity(0.3),
|
||||||
|
fontSize: 120.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-03-05 21:40:51
|
* @Date : 2022-03-05 21:40:51
|
||||||
* @LastEditTime : 2022-03-08 22:19:08
|
* @LastEditTime : 2022-03-11 14:54:02
|
||||||
* @Description : Validation Provider class
|
* @Description : Validation Provider class
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -19,9 +19,15 @@ class InputNotification extends Notification {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ValidationProvider extends StatefulWidget {
|
class ValidationProvider extends StatefulWidget {
|
||||||
const ValidationProvider({Key? key, required this.child}) : super(key: key);
|
static Set<String> validationDatabase = <String>{};
|
||||||
|
|
||||||
|
const ValidationProvider({Key? key, required this.child, required this.database, required this.wordLen, required this.maxChances, required this.gameMode}) : super(key: key);
|
||||||
|
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
final Map<String, List<String>> database;
|
||||||
|
final int wordLen;
|
||||||
|
final int maxChances;
|
||||||
|
final int gameMode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ValidationProvider> createState() => _ValidationProviderState();
|
State<ValidationProvider> createState() => _ValidationProviderState();
|
||||||
@ -38,13 +44,14 @@ class _ValidationProviderState extends State<ValidationProvider> {
|
|||||||
_newGame();
|
_newGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _newGame() async {
|
void _newGame() {
|
||||||
curAttempt = "";
|
curAttempt = "";
|
||||||
curAttemptCount = 0;
|
curAttemptCount = 0;
|
||||||
acceptInput = true;
|
acceptInput = true;
|
||||||
answer = await Words.generateWord();
|
answer = getNextWord(widget.database);
|
||||||
answer = answer.toUpperCase();
|
if(!ValidationProvider.validationDatabase.contains(answer)){
|
||||||
//print(answer);
|
ValidationProvider.validationDatabase.add(answer);
|
||||||
|
}
|
||||||
letterMap = {};
|
letterMap = {};
|
||||||
answer.split('').forEach((c) {
|
answer.split('').forEach((c) {
|
||||||
letterMap[c] ??= 0;
|
letterMap[c] ??= 0;
|
||||||
@ -73,7 +80,7 @@ class _ValidationProviderState extends State<ValidationProvider> {
|
|||||||
builder: (context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('Result'),
|
title: const Text('Result'),
|
||||||
content: Text(result ? "Won" : "Lost, answer is $answer"),
|
content: Text(result ? "Won" : "Lost, answer is ${answer.toLowerCase()}"),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
child: const Text('Back'),
|
child: const Text('Back'),
|
||||||
@ -113,25 +120,25 @@ class _ValidationProviderState extends State<ValidationProvider> {
|
|||||||
child: widget.child,
|
child: widget.child,
|
||||||
onNotification: (noti) {
|
onNotification: (noti) {
|
||||||
if(noti.type == InputType.inputConfirmation) {
|
if(noti.type == InputType.inputConfirmation) {
|
||||||
if(curAttempt.length < 5) {
|
if(curAttempt.length < widget.wordLen) {
|
||||||
//Not enough
|
//Not enough
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//Check validation
|
//Check validation
|
||||||
if(Words.isWordValidate(curAttempt)) {
|
if(ValidationProvider.validationDatabase.lookup(curAttempt) != null) {
|
||||||
//Generate map
|
//Generate map
|
||||||
Map<String, int> leftWordMap = Map.from(letterMap);
|
Map<String, int> leftWordMap = Map.from(letterMap);
|
||||||
var positionValRes = <int>[for(int i = 0; i < 5; i++) -1];
|
var positionValRes = <int>[for(int i = 0; i < widget.wordLen; i++) -1];
|
||||||
var letterValRes = <String, int>{};
|
var letterValRes = <String, int>{};
|
||||||
for(int i = 0; i < 5; i++) {
|
for(int i = 0; i < widget.wordLen; i++) {
|
||||||
if(curAttempt[i] == answer[i]) {
|
if(curAttempt[i] == answer[i]) {
|
||||||
positionValRes[i] = 1;
|
positionValRes[i] = 1;
|
||||||
leftWordMap[curAttempt[i]] = leftWordMap[curAttempt[i]]! - 1;
|
leftWordMap[curAttempt[i]] = leftWordMap[curAttempt[i]]! - 1;
|
||||||
letterValRes[curAttempt[i]] = 1;
|
letterValRes[curAttempt[i]] = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(int i = 0; i < 5; i++) {
|
for(int i = 0; i < widget.wordLen; i++) {
|
||||||
if(curAttempt[i] != answer[i] && leftWordMap[curAttempt[i]] != null && leftWordMap[curAttempt[i]]! > 0) {
|
if(curAttempt[i] != answer[i] && leftWordMap[curAttempt[i]] != null && leftWordMap[curAttempt[i]]! > 0) {
|
||||||
positionValRes[i] = 2;
|
positionValRes[i] = 2;
|
||||||
leftWordMap[curAttempt[i]] = leftWordMap[curAttempt[i]]! - 1;
|
leftWordMap[curAttempt[i]] = leftWordMap[curAttempt[i]]! - 1;
|
||||||
@ -179,7 +186,7 @@ class _ValidationProviderState extends State<ValidationProvider> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
if(acceptInput && curAttempt.length < 5) {
|
if(acceptInput && curAttempt.length < widget.wordLen) {
|
||||||
curAttempt += noti.msg;
|
curAttempt += noti.msg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
99
pubspec.lock
99
pubspec.lock
@ -57,6 +57,20 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.2.0"
|
||||||
|
ffi:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ffi
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.2"
|
||||||
|
file:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "6.1.2"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -109,6 +123,76 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0"
|
version: "1.8.0"
|
||||||
|
path_provider:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: path_provider
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.9"
|
||||||
|
path_provider_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_android
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.12"
|
||||||
|
path_provider_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_ios
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.8"
|
||||||
|
path_provider_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_linux
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.5"
|
||||||
|
path_provider_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_macos
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.5"
|
||||||
|
path_provider_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.3"
|
||||||
|
path_provider_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_windows
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.5"
|
||||||
|
platform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: platform
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.0"
|
||||||
|
plugin_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: plugin_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
process:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: process
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "4.2.4"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -170,5 +254,20 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
|
win32:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.1"
|
||||||
|
xdg_directories:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xdg_directories
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.0+1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.16.1 <3.0.0"
|
dart: ">=2.16.1 <3.0.0"
|
||||||
|
flutter: ">=2.8.0"
|
||||||
|
|||||||
12
pubspec.yaml
12
pubspec.yaml
@ -34,6 +34,7 @@ dependencies:
|
|||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
|
path_provider: ^2.0.9
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@ -62,9 +63,14 @@ flutter:
|
|||||||
# - images/a_dot_burr.jpeg
|
# - images/a_dot_burr.jpeg
|
||||||
# - images/a_dot_ham.jpeg
|
# - images/a_dot_ham.jpeg
|
||||||
assets:
|
assets:
|
||||||
- assets/unixWords.txt
|
- 'assets/All.txt'
|
||||||
- assets/ospd.txt
|
- 'assets/CET4.txt'
|
||||||
- assets/popular.txt
|
- 'assets/CET6.txt'
|
||||||
|
- 'assets/CET46.txt'
|
||||||
|
- 'assets/GRE Slim.txt'
|
||||||
|
- 'assets/TOEFL Slim.txt'
|
||||||
|
- 'assets/TOEFL.txt'
|
||||||
|
- 'assets/HighSchool.txt'
|
||||||
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
# https://flutter.dev/assets-and-images/#resolution-aware.
|
# https://flutter.dev/assets-and-images/#resolution-aware.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user