diff --git a/web/peanut.yaml b/web/peanut.yaml index d3e2abd18..16440125e 100644 --- a/web/peanut.yaml +++ b/web/peanut.yaml @@ -9,7 +9,6 @@ directories: - particle_background/web - slide_puzzle/web - timeflow/web -- vision_challenge/web - form_app/web - web_dashboard/web - place_tracker/web diff --git a/web/samples_index/lib/src/samples.yaml b/web/samples_index/lib/src/samples.yaml index 8964846fc..2d29a1366 100644 --- a/web/samples_index/lib/src/samples.yaml +++ b/web/samples_index/lib/src/samples.yaml @@ -524,24 +524,6 @@ samples: web: web/timeflow type: demo - - name: Vision Challenge - author: Yukkei Choi - screenshots: - - url: images/vision_challenge1.png - alt: Vision Challenge screenshot - - url: images/vision_challenge2.png - alt: Vision Challenge screenshot - source: https://github.com/flutter/samples/tree/master/web/vision_challenge - description: > - A fun game to test your color perception abilities. - difficulty: advanced - widgets: [] - packages: [] - platforms: ['web'] - tags: ['demo', 'game'] - web: web/vision_challenge - type: demo - - name: Dice author: Jaime Blasco screenshots: diff --git a/web/samples_index/web/images/vision_challenge1.png b/web/samples_index/web/images/vision_challenge1.png deleted file mode 100644 index 6b4075c7f..000000000 Binary files a/web/samples_index/web/images/vision_challenge1.png and /dev/null differ diff --git a/web/samples_index/web/images/vision_challenge2.png b/web/samples_index/web/images/vision_challenge2.png deleted file mode 100644 index 3977c2f53..000000000 Binary files a/web/samples_index/web/images/vision_challenge2.png and /dev/null differ diff --git a/web/vision_challenge/LICENSE b/web/vision_challenge/LICENSE deleted file mode 100644 index 39ac907e4..000000000 --- a/web/vision_challenge/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright 2019 Yukkei Choi - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -1. Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/web/vision_challenge/README.md b/web/vision_challenge/README.md deleted file mode 100644 index a24f7b660..000000000 --- a/web/vision_challenge/README.md +++ /dev/null @@ -1,55 +0,0 @@ -A fun game to test your color perception abilities. - -Contributed as part of the Flutter Create 5K challenge by Yukkei Choi. - -## How to play - -Tap the unique color block as fast as possible. - -## Features - -1. Each round when user taps the unique color block, score will be increased by one. -2. Timer: 30 seconds countdown. -3. Color difference will be stepwise reduced when user reached a higher score. -4. If it is difficult to distinguish the unique color block, user can "SHAKE" the device to shift to another theme color, while the position of the unique color block still keep the same. -5. Provide a restart button at the end, user can infinitely play again without relaunching the app. -6. After each replay, game board's theme color will be different from the previous play. -7. Give user a grade based on the final score: - -| score range | grade | -|-------------|-------| -| 0 - 9 | Fail | -| 10 - 19 | D | -| 20 - 29 | C | -| 30 - 34 | B | -| 35 - 39 | B+ | -| 40 - 44 | A | -| 45 or above | A+ | - -## Graphics - -1. I created all graphics used on the app by using Photoshop. -2. Flutter is great and now I'm able to demonstrate my artwork on the app into practice. - -## Techniques used - -1. Use stateful widget to run the timer countdown animation. -2. Since only 5kb is allowed, the grade is calculated by using math, instead of writing if-else statement. -3. Use redux to store the game states: - -| state | description | data type | -|-------|----------------------------------------------------------|-------------------| -| score | Store the player score | int | -| board | Locate the position of unique color block | [[int],[int],...] | -| count | Count the no. of replay, for switching the theme color | int | -| page | Current page / game status | int | - -| page | description | -|------|----------------------------------------------------------------| -| -1 | First launch the app, show the welcome screen with instruction | -| 0 | Game in progress | -| 1 | Game end, show result | - -## Limitation - -Limited to portrait view. diff --git a/web/vision_challenge/assets/10.png b/web/vision_challenge/assets/10.png deleted file mode 100644 index 35513ce15..000000000 Binary files a/web/vision_challenge/assets/10.png and /dev/null differ diff --git a/web/vision_challenge/assets/20.png b/web/vision_challenge/assets/20.png deleted file mode 100644 index d0aeebaf2..000000000 Binary files a/web/vision_challenge/assets/20.png and /dev/null differ diff --git a/web/vision_challenge/assets/30.png b/web/vision_challenge/assets/30.png deleted file mode 100644 index 4f6d6b5b7..000000000 Binary files a/web/vision_challenge/assets/30.png and /dev/null differ diff --git a/web/vision_challenge/assets/35.png b/web/vision_challenge/assets/35.png deleted file mode 100644 index 4cf8bfa29..000000000 Binary files a/web/vision_challenge/assets/35.png and /dev/null differ diff --git a/web/vision_challenge/assets/40.png b/web/vision_challenge/assets/40.png deleted file mode 100644 index ac35e3ab8..000000000 Binary files a/web/vision_challenge/assets/40.png and /dev/null differ diff --git a/web/vision_challenge/assets/45.png b/web/vision_challenge/assets/45.png deleted file mode 100644 index b0be94893..000000000 Binary files a/web/vision_challenge/assets/45.png and /dev/null differ diff --git a/web/vision_challenge/assets/99.png b/web/vision_challenge/assets/99.png deleted file mode 100644 index 0e27ee554..000000000 Binary files a/web/vision_challenge/assets/99.png and /dev/null differ diff --git a/web/vision_challenge/assets/FontManifest.json b/web/vision_challenge/assets/FontManifest.json deleted file mode 100644 index 5921ca028..000000000 --- a/web/vision_challenge/assets/FontManifest.json +++ /dev/null @@ -1,10 +0,0 @@ -[ - { - "family": "MaterialIcons", - "fonts": [ - { - "asset": "https://fonts.gstatic.com/s/materialicons/v42/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2" - } - ] - } -] \ No newline at end of file diff --git a/web/vision_challenge/assets/p0.jpg b/web/vision_challenge/assets/p0.jpg deleted file mode 100644 index b4dabad62..000000000 Binary files a/web/vision_challenge/assets/p0.jpg and /dev/null differ diff --git a/web/vision_challenge/assets/p1.jpg b/web/vision_challenge/assets/p1.jpg deleted file mode 100644 index c688ec887..000000000 Binary files a/web/vision_challenge/assets/p1.jpg and /dev/null differ diff --git a/web/vision_challenge/assets/preview.png b/web/vision_challenge/assets/preview.png deleted file mode 100644 index 7b2b98ed1..000000000 Binary files a/web/vision_challenge/assets/preview.png and /dev/null differ diff --git a/web/vision_challenge/lib/game.dart b/web/vision_challenge/lib/game.dart deleted file mode 100644 index 3cfcdcf98..000000000 --- a/web/vision_challenge/lib/game.dart +++ /dev/null @@ -1,207 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:flutter_redux/flutter_redux.dart'; -import 'package:redux/redux.dart'; - -setText(text, size, color) => Text(text, - style: TextStyle( - fontSize: size, - color: color, - fontWeight: FontWeight.bold, - decoration: TextDecoration.none)); - -pad(double left, double top) => EdgeInsets.fromLTRB(left, top, 0, 0); - -setBg(name) => BoxDecoration( - image: DecorationImage( - fit: BoxFit.cover, - alignment: Alignment.topLeft, - image: AssetImage(name))); - -class Game extends StatelessWidget { - final Store store; - Game(this.store); - _grade(int score) => [10, 20, 30, 35, 40, 45, 99] - .where((i) => i > score) - .reduce(min) - .toString(); - - _createBoard(double size, List> blocks, int depth, - MaterialColor color) => - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: blocks - .map((cols) => Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: cols - .map((item) => Flexible( - child: GestureDetector( - onTap: () { - if (item == 1) store.dispatch(Action.next); - }, - child: Container( - width: size, - height: size, - color: item > 0 ? color[depth] : color)), - )) - .toList())) - .toList()); - - @override - Widget build(BuildContext context) => StoreConnector( - // onInit: (state) => ShakeDetector.autoStart( - // onPhoneShake: () => store.dispatch(Action.shake)), - converter: (store) => store.state, - builder: (context, state) { - var w = MediaQuery.of(context).size.height / 16 * 9, - size = w / (state.board.length + 1), - depth = [1 + state.score ~/ 5, 4].reduce(min) * 100, - colors = [ - Colors.blue, - Colors.orange, - Colors.pink, - Colors.purple, - Colors.cyan - ]; - - return Scaffold( - backgroundColor: Color(0xFFBCE1F6), - body: Center( - child: SizedBox( - height: MediaQuery.of(context).size.height, - width: MediaQuery.of(context).size.height / 16 * 9, - child: Container( - decoration: setBg(state.page < 0 ? 'p0.jpg' : 'p1.jpg'), - child: state.page >= 0 - ? Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - height: w * 0.325, - padding: pad(0, w * 0.145), - child: setText(state.score.toString(), - w * 0.2, Colors.white)), - Container( - height: w * 0.35, - padding: pad(w * 0.69, state.page * 7.0), - child: state.page < 1 - ? Timer( - onEnd: () => - store.dispatch(Action.end), - width: w) - : setText('End', w * 0.08, Colors.red)), - state.page < 1 - ? Container( - width: w, - height: w * 1.05, - padding: pad(0, w * 0.05), - child: _createBoard( - size, - state.board, - depth, - colors[ - state.count % colors.length])) - : Container( - width: w, - height: w, - decoration: - setBg(_grade(state.score) + '.png')) - ]) - : Container()), - ), - ), - floatingActionButton: state.page != 0 - ? Container( - // width: w * 0.2, - // height: w * 0.2, - child: FloatingActionButton( - child: Icon( - state.page < 1 ? Icons.play_arrow : Icons.refresh), - onPressed: () => store.dispatch(Action.start))) - : Container()); - }); -} - -class Timer extends StatefulWidget { - Timer({this.onEnd, this.width}); - final VoidCallback onEnd; - final double width; - @override - _TimerState createState() => _TimerState(); -} - -class _TimerState extends State with TickerProviderStateMixin { - Animation _animate; - int _sec = 31; - - @override - void initState() { - super.initState(); - _animate = StepTween(begin: _sec, end: 0).animate( - AnimationController(duration: Duration(seconds: _sec), vsync: this) - ..forward(from: 0.0)) - ..addStatusListener((AnimationStatus s) { - if (s == AnimationStatus.completed) widget.onEnd(); - }); - } - - @override - Widget build(BuildContext context) => AnimatedBuilder( - animation: _animate, - builder: (BuildContext context, Widget child) => setText( - _animate.value.toString().padLeft(2, '0'), - widget.width * 0.12, - Colors.green)); -} - -//REDUX -@immutable -class AppState { - final int score, page, count; - final List> board; - AppState({this.score, this.page, this.board, this.count}); - AppState.init() - : score = 0, - page = -1, - count = 0, - board = newBoard(0); -} - -enum Action { next, end, start, shake } - -AppState reducer(AppState s, act) { - switch (act) { - case Action.next: - return AppState( - score: s.score + 1, - page: s.page, - count: s.count, - board: newBoard(s.score + 1)); - case Action.end: - return AppState( - score: s.score, page: 1, count: s.count + 1, board: s.board); - case Action.start: - return AppState(score: 0, page: 0, count: s.count, board: newBoard(0)); - case Action.shake: - return AppState( - score: s.score, page: s.page, count: s.count + 1, board: s.board); - default: - return s; - } -} - -List> newBoard(score) { - var size = score < 7 ? score + 3 : 10, - rng = Random(), - bingoRow = rng.nextInt(size), - bingoCol = rng.nextInt(size); - List> board = []; - for (var i = 0; i < size; i++) { - List row = []; - for (var j = 0; j < size; j++) - row.add(i == bingoRow && j == bingoCol ? 1 : 0); - board.add(row); - } - return board; -} diff --git a/web/vision_challenge/lib/main.dart b/web/vision_challenge/lib/main.dart deleted file mode 100644 index 60bda4aed..000000000 --- a/web/vision_challenge/lib/main.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_redux/flutter_redux.dart'; -import 'package:redux/redux.dart'; -import 'game.dart'; - -void main() { - final store = Store( - reducer, - initialState: AppState.init(), - ); - - runApp( - StoreProvider( - store: store, - child: MaterialApp( - home: Game(store), - ), - ), - ); -} diff --git a/web/vision_challenge/pubspec.lock b/web/vision_challenge/pubspec.lock deleted file mode 100644 index 947fecc39..000000000 --- a/web/vision_challenge/pubspec.lock +++ /dev/null @@ -1,64 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - characters: - dependency: transitive - description: - name: characters - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0-nullsafety.5" - collection: - dependency: transitive - description: - name: collection - url: "https://pub.dartlang.org" - source: hosted - version: "1.15.0-nullsafety.5" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_redux: - dependency: "direct main" - description: - name: flutter_redux - url: "https://pub.dartlang.org" - source: hosted - version: "0.5.4" - meta: - dependency: transitive - description: - name: meta - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0-nullsafety.6" - redux: - dependency: "direct main" - description: - name: redux - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.0" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0-nullsafety.5" - vector_math: - dependency: transitive - description: - name: vector_math - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0-nullsafety.5" -sdks: - dart: ">=2.12.0-0 <3.0.0" diff --git a/web/vision_challenge/pubspec.yaml b/web/vision_challenge/pubspec.yaml deleted file mode 100644 index 9ada7fc89..000000000 --- a/web/vision_challenge/pubspec.yaml +++ /dev/null @@ -1,25 +0,0 @@ -name: vision_challenge -author: Yukkei Choi - -environment: - sdk: ">=2.10.0 <3.0.0" - -dependencies: - flutter: - sdk: flutter - redux: ^3.0.0 - flutter_redux: ^0.5.3 - -flutter: - uses-material-design: true - assets: - - 10.png - - 20.png - - 30.png - - 35.png - - 40.png - - 45.png - - 99.png - - p0.jpg - - p1.jpg - - preview.png diff --git a/web/vision_challenge/web/index.html b/web/vision_challenge/web/index.html deleted file mode 100644 index 1785ebb8b..000000000 --- a/web/vision_challenge/web/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - -