1
0
mirror of https://github.com/flutter/samples.git synced 2026-03-30 08:11:40 +00:00

Add flutter_web samples (#75)

This commit is contained in:
Kevin Moore
2019-05-07 13:32:08 -07:00
committed by Andrew Brogdon
parent 42f2dce01b
commit 3fe927cb29
697 changed files with 241026 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
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.

View File

@@ -0,0 +1,55 @@
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.

View File

@@ -0,0 +1,207 @@
import 'dart:math';
import 'package:flutter_web/material.dart';
import 'package:vision_challenge/packages/flutter_redux.dart';
import 'package:vision_challenge/packages/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<AppState> 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<List<int>> 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<AppState, AppState>(
// 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: <Widget>[
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<Timer> 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<List<int>> 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<List<int>> newBoard(score) {
var size = score < 7 ? score + 3 : 10,
rng = Random(),
bingoRow = rng.nextInt(size),
bingoCol = rng.nextInt(size);
List<List<int>> board = [];
for (var i = 0; i < size; i++) {
List<int> row = [];
for (var j = 0; j < size; j++)
row.add(i == bingoRow && j == bingoCol ? 1 : 0);
board.add(row);
}
return board;
}

View File

@@ -0,0 +1,20 @@
import 'package:flutter_web/material.dart';
import 'package:vision_challenge/packages/flutter_redux.dart';
import 'package:vision_challenge/packages/redux.dart';
import 'game.dart';
void main() {
final store = Store<AppState>(
reducer,
initialState: AppState.init(),
);
runApp(
StoreProvider<AppState>(
store: store,
child: MaterialApp(
home: Game(store),
),
),
);
}

View File

@@ -0,0 +1,513 @@
// Package flutter_redux:
// https://pub.dev/packages/flutter_redux
import 'dart:async';
import 'package:flutter_web/widgets.dart';
import 'package:meta/meta.dart';
import 'redux.dart';
/// Provides a Redux [Store] to all descendants of this Widget. This should
/// generally be a root widget in your App. Connect to the Store provided
/// by this Widget using a [StoreConnector] or [StoreBuilder].
class StoreProvider<S> extends InheritedWidget {
final Store<S> _store;
/// Create a [StoreProvider] by passing in the required [store] and [child]
/// parameters.
const StoreProvider({
Key key,
@required Store<S> store,
@required Widget child,
}) : assert(store != null),
assert(child != null),
_store = store,
super(key: key, child: child);
/// A method that can be called by descendant Widgets to retrieve the Store
/// from the StoreProvider.
///
/// Important: When using this method, pass through complete type information
/// or Flutter will be unable to find the correct StoreProvider!
///
/// ### Example
///
/// ```
/// class MyWidget extends StatelessWidget {
/// @override
/// Widget build(BuildContext context) {
/// final store = StoreProvider.of<int>(context);
///
/// return Text('${store.state}');
/// }
/// }
/// ```
static Store<S> of<S>(BuildContext context) {
final type = _typeOf<StoreProvider<S>>();
final provider =
context.inheritFromWidgetOfExactType(type) as StoreProvider<S>;
if (provider == null) throw StoreProviderError(type);
return provider._store;
}
// Workaround to capture generics
static Type _typeOf<T>() => T;
@override
bool updateShouldNotify(StoreProvider<S> oldWidget) =>
_store != oldWidget._store;
}
/// Build a Widget using the [BuildContext] and [ViewModel]. The [ViewModel] is
/// derived from the [Store] using a [StoreConverter].
typedef ViewModelBuilder<ViewModel> = Widget Function(
BuildContext context,
ViewModel vm,
);
/// Convert the entire [Store] into a [ViewModel]. The [ViewModel] will be used
/// to build a Widget using the [ViewModelBuilder].
typedef StoreConverter<S, ViewModel> = ViewModel Function(
Store<S> store,
);
/// A function that will be run when the [StoreConnector] is initialized (using
/// the [State.initState] method). This can be useful for dispatching actions
/// that fetch data for your Widget when it is first displayed.
typedef OnInitCallback<S> = void Function(
Store<S> store,
);
/// A function that will be run when the StoreConnector is removed from the
/// Widget Tree.
///
/// It is run in the [State.dispose] method.
///
/// This can be useful for dispatching actions that remove stale data from
/// your State tree.
typedef OnDisposeCallback<S> = void Function(
Store<S> store,
);
/// A test of whether or not your `converter` function should run in response
/// to a State change. For advanced use only.
///
/// Some changes to the State of your application will mean your `converter`
/// function can't produce a useful ViewModel. In these cases, such as when
/// performing exit animations on data that has been removed from your Store,
/// it can be best to ignore the State change while your animation completes.
///
/// To ignore a change, provide a function that returns true or false. If the
/// returned value is true, the change will be ignored.
///
/// If you ignore a change, and the framework needs to rebuild the Widget, the
/// `builder` function will be called with the latest `ViewModel` produced by
/// your `converter` function.
typedef IgnoreChangeTest<S> = bool Function(S state);
/// A function that will be run on State change, before the build method.
///
/// This function is passed the `ViewModel`, and if `distinct` is `true`,
/// it will only be called if the `ViewModel` changes.
///
/// This can be useful for imperative calls to things like Navigator,
/// TabController, etc
typedef OnWillChangeCallback<ViewModel> = void Function(ViewModel viewModel);
/// A function that will be run on State change, after the build method.
///
/// This function is passed the `ViewModel`, and if `distinct` is `true`,
/// it will only be called if the `ViewModel` changes.
///
/// This can be useful for running certain animations after the build is
/// complete.
///
/// Note: Using a [BuildContext] inside this callback can cause problems if
/// the callback performs navigation. For navigation purposes, please use
/// an [OnWillChangeCallback].
typedef OnDidChangeCallback<ViewModel> = void Function(ViewModel viewModel);
/// A function that will be run after the Widget is built the first time.
///
/// This function is passed the initial `ViewModel` created by the `converter`
/// function.
///
/// This can be useful for starting certain animations, such as showing
/// Snackbars, after the Widget is built the first time.
typedef OnInitialBuildCallback<ViewModel> = void Function(ViewModel viewModel);
/// Build a widget based on the state of the [Store].
///
/// Before the [builder] is run, the [converter] will convert the store into a
/// more specific `ViewModel` tailored to the Widget being built.
///
/// Every time the store changes, the Widget will be rebuilt. As a performance
/// optimization, the Widget can be rebuilt only when the [ViewModel] changes.
/// In order for this to work correctly, you must implement [==] and [hashCode]
/// for the [ViewModel], and set the [distinct] option to true when creating
/// your StoreConnector.
class StoreConnector<S, ViewModel> extends StatelessWidget {
/// Build a Widget using the [BuildContext] and [ViewModel]. The [ViewModel]
/// is created by the [converter] function.
final ViewModelBuilder<ViewModel> builder;
/// Convert the [Store] into a [ViewModel]. The resulting [ViewModel] will be
/// passed to the [builder] function.
final StoreConverter<S, ViewModel> converter;
/// As a performance optimization, the Widget can be rebuilt only when the
/// [ViewModel] changes. In order for this to work correctly, you must
/// implement [==] and [hashCode] for the [ViewModel], and set the [distinct]
/// option to true when creating your StoreConnector.
final bool distinct;
/// A function that will be run when the StoreConnector is initially created.
/// It is run in the [State.initState] method.
///
/// This can be useful for dispatching actions that fetch data for your Widget
/// when it is first displayed.
final OnInitCallback<S> onInit;
/// A function that will be run when the StoreConnector is removed from the
/// Widget Tree.
///
/// It is run in the [State.dispose] method.
///
/// This can be useful for dispatching actions that remove stale data from
/// your State tree.
final OnDisposeCallback<S> onDispose;
/// Determines whether the Widget should be rebuilt when the Store emits an
/// onChange event.
final bool rebuildOnChange;
/// A test of whether or not your [converter] function should run in response
/// to a State change. For advanced use only.
///
/// Some changes to the State of your application will mean your [converter]
/// function can't produce a useful ViewModel. In these cases, such as when
/// performing exit animations on data that has been removed from your Store,
/// it can be best to ignore the State change while your animation completes.
///
/// To ignore a change, provide a function that returns true or false. If the
/// returned value is true, the change will be ignored.
///
/// If you ignore a change, and the framework needs to rebuild the Widget, the
/// [builder] function will be called with the latest [ViewModel] produced by
/// your [converter] function.
final IgnoreChangeTest<S> ignoreChange;
/// A function that will be run on State change, before the Widget is built.
///
/// This function is passed the `ViewModel`, and if `distinct` is `true`,
/// it will only be called if the `ViewModel` changes.
///
/// This can be useful for imperative calls to things like Navigator,
/// TabController, etc
final OnWillChangeCallback<ViewModel> onWillChange;
/// A function that will be run on State change, after the Widget is built.
///
/// This function is passed the `ViewModel`, and if `distinct` is `true`,
/// it will only be called if the `ViewModel` changes.
///
/// This can be useful for running certain animations after the build is
/// complete.
///
/// Note: Using a [BuildContext] inside this callback can cause problems if
/// the callback performs navigation. For navigation purposes, please use
/// [onWillChange].
final OnDidChangeCallback<ViewModel> onDidChange;
/// A function that will be run after the Widget is built the first time.
///
/// This function is passed the initial `ViewModel` created by the [converter]
/// function.
///
/// This can be useful for starting certain animations, such as showing
/// Snackbars, after the Widget is built the first time.
final OnInitialBuildCallback<ViewModel> onInitialBuild;
/// Create a [StoreConnector] by passing in the required [converter] and
/// [builder] functions.
///
/// You can also specify a number of additional parameters that allow you to
/// modify the behavior of the StoreConnector. Please see the documentation
/// for each option for more info.
StoreConnector({
Key key,
@required this.builder,
@required this.converter,
this.distinct = false,
this.onInit,
this.onDispose,
this.rebuildOnChange = true,
this.ignoreChange,
this.onWillChange,
this.onDidChange,
this.onInitialBuild,
}) : assert(builder != null),
assert(converter != null),
super(key: key);
@override
Widget build(BuildContext context) {
return _StoreStreamListener<S, ViewModel>(
store: StoreProvider.of<S>(context),
builder: builder,
converter: converter,
distinct: distinct,
onInit: onInit,
onDispose: onDispose,
rebuildOnChange: rebuildOnChange,
ignoreChange: ignoreChange,
onWillChange: onWillChange,
onDidChange: onDidChange,
onInitialBuild: onInitialBuild,
);
}
}
/// Build a Widget by passing the [Store] directly to the build function.
///
/// Generally, it's considered best practice to use the [StoreConnector] and to
/// build a `ViewModel` specifically for your Widget rather than passing through
/// the entire [Store], but this is provided for convenience when that isn't
/// necessary.
class StoreBuilder<S> extends StatelessWidget {
static Store<S> _identity<S>(Store<S> store) => store;
/// Builds a Widget using the [BuildContext] and your [Store].
final ViewModelBuilder<Store<S>> builder;
/// Indicates whether or not the Widget should rebuild when the [Store] emits
/// an `onChange` event.
final bool rebuildOnChange;
/// A function that will be run when the StoreConnector is initially created.
/// It is run in the [State.initState] method.
///
/// This can be useful for dispatching actions that fetch data for your Widget
/// when it is first displayed.
final OnInitCallback<S> onInit;
/// A function that will be run when the StoreBuilder is removed from the
/// Widget Tree.
///
/// It is run in the [State.dispose] method.
///
/// This can be useful for dispatching actions that remove stale data from
/// your State tree.
final OnDisposeCallback<S> onDispose;
/// A function that will be run on State change, before the Widget is built.
///
/// This can be useful for imperative calls to things like Navigator,
/// TabController, etc
final OnWillChangeCallback<Store<S>> onWillChange;
/// A function that will be run on State change, after the Widget is built.
///
/// This can be useful for running certain animations after the build is
/// complete
///
/// Note: Using a [BuildContext] inside this callback can cause problems if
/// the callback performs navigation. For navigation purposes, please use
/// [onWillChange].
final OnDidChangeCallback<Store<S>> onDidChange;
/// A function that will be run after the Widget is built the first time.
///
/// This can be useful for starting certain animations, such as showing
/// Snackbars, after the Widget is built the first time.
final OnInitialBuildCallback<Store<S>> onInitialBuild;
/// Create's a Widget based on the Store.
StoreBuilder({
Key key,
@required this.builder,
this.onInit,
this.onDispose,
this.rebuildOnChange = true,
this.onWillChange,
this.onDidChange,
this.onInitialBuild,
}) : assert(builder != null),
super(key: key);
@override
Widget build(BuildContext context) {
return StoreConnector<S, Store<S>>(
builder: builder,
converter: _identity,
rebuildOnChange: rebuildOnChange,
onInit: onInit,
onDispose: onDispose,
onWillChange: onWillChange,
onDidChange: onDidChange,
onInitialBuild: onInitialBuild,
);
}
}
/// Listens to the [Store] and calls [builder] whenever [store] changes.
class _StoreStreamListener<S, ViewModel> extends StatefulWidget {
final ViewModelBuilder<ViewModel> builder;
final StoreConverter<S, ViewModel> converter;
final Store<S> store;
final bool rebuildOnChange;
final bool distinct;
final OnInitCallback<S> onInit;
final OnDisposeCallback<S> onDispose;
final IgnoreChangeTest<S> ignoreChange;
final OnWillChangeCallback<ViewModel> onWillChange;
final OnDidChangeCallback<ViewModel> onDidChange;
final OnInitialBuildCallback<ViewModel> onInitialBuild;
_StoreStreamListener({
Key key,
@required this.builder,
@required this.store,
@required this.converter,
this.distinct = false,
this.onInit,
this.onDispose,
this.rebuildOnChange = true,
this.ignoreChange,
this.onWillChange,
this.onDidChange,
this.onInitialBuild,
}) : super(key: key);
@override
State<StatefulWidget> createState() {
return _StoreStreamListenerState<S, ViewModel>();
}
}
class _StoreStreamListenerState<S, ViewModel>
extends State<_StoreStreamListener<S, ViewModel>> {
Stream<ViewModel> stream;
ViewModel latestValue;
@override
void initState() {
_init();
super.initState();
}
@override
void dispose() {
if (widget.onDispose != null) {
widget.onDispose(widget.store);
}
super.dispose();
}
@override
void didUpdateWidget(_StoreStreamListener<S, ViewModel> oldWidget) {
if (widget.store != oldWidget.store) {
_init();
}
super.didUpdateWidget(oldWidget);
}
void _init() {
if (widget.onInit != null) {
widget.onInit(widget.store);
}
latestValue = widget.converter(widget.store);
if (widget.onInitialBuild != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.onInitialBuild(latestValue);
});
}
var _stream = widget.store.onChange;
if (widget.ignoreChange != null) {
_stream = _stream.where((state) => !widget.ignoreChange(state));
}
stream = _stream.map((_) => widget.converter(widget.store));
// Don't use `Stream.distinct` because it cannot capture the initial
// ViewModel produced by the `converter`.
if (widget.distinct) {
stream = stream.where((vm) {
final isDistinct = vm != latestValue;
return isDistinct;
});
}
// After each ViewModel is emitted from the Stream, we update the
// latestValue. Important: This must be done after all other optional
// transformations, such as ignoreChange.
stream =
stream.transform(StreamTransformer.fromHandlers(handleData: (vm, sink) {
latestValue = vm;
if (widget.onWillChange != null) {
widget.onWillChange(latestValue);
}
if (widget.onDidChange != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.onDidChange(latestValue);
});
}
sink.add(vm);
}));
}
@override
Widget build(BuildContext context) {
return widget.rebuildOnChange
? StreamBuilder<ViewModel>(
stream: stream,
builder: (context, snapshot) => widget.builder(
context,
snapshot.hasData ? snapshot.data : latestValue,
),
)
: widget.builder(context, latestValue);
}
}
/// If the StoreProvider.of method fails, this error will be thrown.
///
/// Often, when the `of` method fails, it is difficult to understand why since
/// there can be multiple causes. This error explains those causes so the user
/// can understand and fix the issue.
class StoreProviderError extends Error {
/// The type of the class the user tried to retrieve
Type type;
/// Creates a StoreProviderError
StoreProviderError(this.type);
@override
String toString() {
return '''Error: No $type found. To fix, please try:
* Wrapping your MaterialApp with the StoreProvider<State>,
rather than an individual Route
* Providing full type information to your Store<State>,
StoreProvider<State> and StoreConnector<State, ViewModel>
* Ensure you are using consistent and complete imports.
E.g. always use `import 'package:my_app/app_state.dart';
If none of these solutions work, please file a bug at:
https://github.com/brianegan/flutter_redux/issues/new
''';
}
}

View File

@@ -0,0 +1,519 @@
// Package redux:
// https://pub.dev/packages/redux
import 'dart:async';
/// Defines an application's state change
///
/// Implement this typedef to modify your app state in response to a given
/// action.
///
/// ### Example
///
/// int counterReducer(int state, action) {
/// switch (action) {
/// case 'INCREMENT':
/// return state + 1;
/// case 'DECREMENT':
/// return state - 1;
/// default:
/// return state;
/// }
/// }
///
/// final store = new Store<int>(counterReducer);
typedef Reducer<State> = State Function(State state, dynamic action);
/// Defines a [Reducer] using a class interface.
///
/// Implement this class to modify your app state in response to a given action.
///
/// For some use cases, a class may be preferred to a function. In these
/// instances, a ReducerClass can be used.
///
/// ### Example
///
/// class CounterReducer extends ReducerClass<int> {
/// int call(int state, action) {
/// switch (action) {
/// case 'INCREMENT':
/// return state + 1;
/// case 'DECREMENT':
/// return state - 1;
/// default:
/// return state;
/// }
/// }
/// }
///
/// final store = new Store<int>(new CounterReducer());
abstract class ReducerClass<State> {
State call(State state, dynamic action);
}
/// A function that intercepts actions and potentially transform actions before
/// they reach the reducer.
///
/// Middleware intercept actions before they reach the reducer. This gives them
/// the ability to produce side-effects or modify the passed in action before
/// they reach the reducer.
///
/// ### Example
///
/// loggingMiddleware(Store<int> store, action, NextDispatcher next) {
/// print('${new DateTime.now()}: $action');
///
/// next(action);
/// }
///
/// // Create your store with the loggingMiddleware
/// final store = new Store<int>(
/// counterReducer,
/// middleware: [loggingMiddleware],
/// );
typedef Middleware<State> = void Function(
Store<State> store,
dynamic action,
NextDispatcher next,
);
/// Defines a [Middleware] using a Class interface.
///
/// Middleware intercept actions before they reach the reducer. This gives them
/// the ability to produce side-effects or modify the passed in action before
/// they reach the reducer.
///
/// For some use cases, a class may be preferred to a function. In these
/// instances, a MiddlewareClass can be used.
///
/// ### Example
/// class LoggingMiddleware extends MiddlewareClass<int> {
/// call(Store<int> store, action, NextDispatcher next) {
/// print('${new DateTime.now()}: $action');
///
/// next(action);
/// }
/// }
///
/// // Create your store with the loggingMiddleware
/// final store = new Store<int>(
/// counterReducer,
/// middleware: [new LoggingMiddleware()],
/// );
abstract class MiddlewareClass<State> {
void call(Store<State> store, dynamic action, NextDispatcher next);
}
/// The contract between one piece of middleware and the next in the chain. Use
/// it to send the current action in your [Middleware] to the next piece of
/// [Middleware] in the chain.
///
/// Middleware can optionally pass the original action or a modified action to
/// the next piece of middleware, or never call the next piece of middleware at
/// all.
typedef NextDispatcher = void Function(dynamic action);
/// Creates a Redux store that holds the app state tree.
///
/// The only way to change the state tree in the store is to [dispatch] an
/// action. the action will then be intercepted by any provided [Middleware].
/// After running through the middleware, the action will be sent to the given
/// [Reducer] to update the state tree.
///
/// To access the state tree, call the [state] getter or listen to the
/// [onChange] stream.
///
/// ### Basic Example
///
/// // Create a reducer
/// final increment = 'INCREMENT';
/// final decrement = 'DECREMENT';
///
/// int counterReducer(int state, action) {
/// switch (action) {
/// case increment:
/// return state + 1;
/// case decrement:
/// return state - 1;
/// default:
/// return state;
/// }
/// }
///
/// // Create the store
/// final store = new Store<int>(counterReducer, initialState: 0);
///
/// // Print the Store's state.
/// print(store.state); // prints "0"
///
/// // Dispatch an action. This will be sent to the reducer to update the
/// // state.
/// store.dispatch(increment);
///
/// // Print the updated state. As an alternative, you can use the
/// // `store.onChange.listen` to respond to all state change events.
/// print(store.state); // prints "1"
class Store<State> {
/// The [Reducer] for your Store. Allows you to get the current reducer or
/// replace it with a new one if need be.
Reducer<State> reducer;
final StreamController<State> _changeController;
State _state;
List<NextDispatcher> _dispatchers;
Store(
this.reducer, {
State initialState,
List<Middleware<State>> middleware = const [],
bool syncStream = false,
/// If set to true, the Store will not emit onChange events if the new State
/// that is returned from your [reducer] in response to an Action is equal
/// to the previous state.
///
/// Under the hood, it will use the `==` method from your State class to
/// determine whether or not the two States are equal.
bool distinct = false,
}) : _changeController = StreamController.broadcast(sync: syncStream) {
_state = initialState;
_dispatchers = _createDispatchers(
middleware,
_createReduceAndNotify(distinct),
);
}
/// Returns the current state of the app
State get state => _state;
/// A stream that emits the current state when it changes.
///
/// ### Example
///
/// // First, create the Store
/// final store = new Store<int>(counterReducer, 0);
///
/// // Next, listen to the Store's onChange stream, and print the latest
/// // state to your console whenever the reducer produces a new State.
/// //
/// // We'll store the StreamSubscription as a variable so we can stop
/// // listening later.
/// final subscription = store.onChange.listen(print);
///
/// // Dispatch some actions, and see the printing magic!
/// store.dispatch("INCREMENT"); // prints 1
/// store.dispatch("INCREMENT"); // prints 2
/// store.dispatch("DECREMENT"); // prints 1
///
/// // When you want to stop printing the state to the console, simply
/// `cancel` your `subscription`.
/// subscription.cancel();
Stream<State> get onChange => _changeController.stream;
// Creates the base [NextDispatcher].
//
// The base NextDispatcher will be called after all other middleware provided
// by the user have been run. Its job is simple: Run the current state through
// the reducer, save the result, and notify any subscribers.
NextDispatcher _createReduceAndNotify(bool distinct) {
return (dynamic action) {
final state = reducer(_state, action);
if (distinct && state == _state) return;
_state = state;
_changeController.add(state);
};
}
List<NextDispatcher> _createDispatchers(
List<Middleware<State>> middleware,
NextDispatcher reduceAndNotify,
) {
final dispatchers = <NextDispatcher>[]..add(reduceAndNotify);
// Convert each [Middleware] into a [NextDispatcher]
for (var nextMiddleware in middleware.reversed) {
final next = dispatchers.last;
dispatchers.add(
(dynamic action) => nextMiddleware(this, action, next),
);
}
return dispatchers.reversed.toList();
}
/// Runs the action through all provided [Middleware], then applies an action
/// to the state using the given [Reducer]. Please note: [Middleware] can
/// intercept actions, and can modify actions or stop them from passing
/// through to the reducer.
void dispatch(dynamic action) {
_dispatchers[0](action);
}
/// Closes down the Store so it will no longer be operational. Only use this
/// if you want to destroy the Store while your app is running. Do not use
/// this method as a way to stop listening to [onChange] state changes. For
/// that purpose, view the [onChange] documentation.
Future teardown() async {
_state = null;
return _changeController.close();
}
}
/// A convenience class for binding Reducers to Actions of a given Type. This
/// allows for type safe [Reducer]s and reduces boilerplate.
///
/// ### Example
///
/// In order to see what this utility function does, let's take a look at a
/// regular example of using reducers based on the Type of an action.
///
/// ```
/// // We define out State and Action classes.
/// class AppState {
/// final List<Item> items;
///
/// AppState(this.items);
/// }
///
/// class LoadItemsAction {}
/// class UpdateItemsAction {}
/// class AddItemAction{}
/// class RemoveItemAction {}
/// class ShuffleItemsAction {}
/// class ReverseItemsAction {}
/// class ItemsLoadedAction<Item> {
/// final List<Item> items;
///
/// ItemsLoadedAction(this.items);
/// }
///
/// // Then we define our reducer. Since we handle different actions in our
/// // reducer, we need to determine what kind of action we're working with
/// // using if statements, and then run some computation in response.
/// //
/// // This isn't a big deal if we have relatively few cases to handle, but your
/// // reducer function can quickly grow large and take on too many
/// // responsibilities as demonstrated here with pseudo-code.
/// final appReducer = (AppState state, action) {
/// if (action is ItemsLoadedAction) {
/// return new AppState(action.items);
/// } else if (action is UpdateItemsAction) {
/// return ...;
/// } else if (action is AddItemAction) {
/// return ...;
/// } else if (action is RemoveItemAction) {
/// return ...;
/// } else if (action is ShuffleItemsAction) {
/// return ...;
/// } else if (action is ReverseItemsAction) {
/// return ...;
/// } else {
/// return state;
/// }
/// };
/// ```
///
/// What would be nice would be to break our big reducer up into smaller
/// reducers. It would also be nice to bind specific Types of Actions to
/// specific reducers so we can ensure type safety for our reducers while
/// avoiding large trees of `if` statements.
///
/// ```
/// // First, we'll break out all of our individual State Changes into
/// // individual reducers. These can be easily tested or composed!
/// final loadItemsReducer = (AppState state, LoadTodosAction action) =>
/// return new AppState(action.items);
///
/// final updateItemsReducer = (AppState state, UpdateItemsAction action) {
/// return ...;
/// }
///
/// final addItemReducer = (AppState state, AddItemAction action) {
/// return ...;
/// }
///
/// final removeItemReducer = (AppState state, RemoveItemAction action) {
/// return ...;
/// }
///
/// final shuffleItemsReducer = (AppState state, ShuffleItemAction action) {
/// return ...;
/// }
///
/// final reverseItemsReducer = (AppState state, ReverseItemAction action) {
/// return ...;
/// }
///
/// // We will then wire up specific types of actions to our reducer functions
/// // above. This will return a new Reducer<AppState> which puts everything
/// // together!.
/// final Reducer<AppState> appReducer = combineReducers([
/// new TypedReducer<AppState, LoadTodosAction>(loadItemsReducer),
/// new TypedReducer<AppState, UpdateItemsAction>(updateItemsReducer),
/// new TypedReducer<AppState, AddItemAction>(addItemReducer),
/// new TypedReducer<AppState, RemoveItemAction>(removeItemReducer),
/// new TypedReducer<AppState, ShuffleItemAction>(shuffleItemsReducer),
/// new TypedReducer<AppState, ReverseItemAction>(reverseItemsReducer),
/// ]);
/// ```
class TypedReducer<State, Action> implements ReducerClass<State> {
final State Function(State state, Action action) reducer;
TypedReducer(this.reducer);
@override
State call(State state, dynamic action) {
if (action is Action) {
return reducer(state, action);
}
return state;
}
}
/// A convenience type for binding a piece of Middleware to an Action
/// of a specific type. Allows for Type Safe Middleware and reduces boilerplate.
///
/// ### Example
///
/// In order to see what this utility function does, let's take a look at a
/// regular example of running Middleware based on the Type of an action.
///
/// ```
/// class AppState {
/// final List<Item> items;
///
/// AppState(this.items);
/// }
/// class LoadItemsAction {}
/// class UpdateItemsAction {}
/// class AddItemAction{}
/// class RemoveItemAction {}
/// class ShuffleItemsAction {}
/// class ReverseItemsAction {}
/// class ItemsLoadedAction<Item> {
/// final List<Item> items;
///
/// ItemsLoadedAction(this.items);
/// }
///
/// final loadItems = () { /* Function that loads a Future<List<Item>> */}
/// final saveItems = (List<Item> items) { /* Function that persists items */}
///
/// final middleware = (Store<AppState> store, action, NextDispatcher next) {
/// if (action is LoadItemsAction) {
/// loadItems()
/// .then((items) => store.dispatch(new ItemsLoaded(items))
/// .catchError((_) => store.dispatch(new ItemsNotLoaded());
///
/// next(action);
/// } else if (action is UpdateItemsAction ||
/// action is AddItemAction ||
/// action is RemoveItemAction ||
/// action is ShuffleItemsAction ||
/// action is ReverseItemsAction) {
/// next(action);
///
/// saveItems(store.state.items);
/// } else {
/// next(action);
/// }
/// };
/// ```
///
/// This works fine if you have one or two actions to handle, but you might
/// notice it's getting a bit messy already. Let's see how this lib helps clean
/// it up.
///
/// ```
/// // First, let's start by breaking up our functionality into two middleware
/// // functions.
/// //
/// // The loadItemsMiddleware will only handle the `LoadItemsAction`s that
/// // are dispatched, so we can annotate the Type of action.
/// final loadItemsMiddleware = (
/// Store<AppState> store,
/// LoadItemsAction action,
/// NextDispatcher next,
/// ) {
/// loadItems()
/// .then((items) => store.dispatch(new ItemsLoaded(items))
/// .catchError((_) => store.dispatch(new ItemsNotLoaded());
///
/// next(action);
/// }
///
/// // The saveItemsMiddleware handles all actions that change the Items, but
/// // does not depend on the payload of the action. Therefore, `action` will
/// // remain dynamic.
/// final saveItemsMiddleware = (
/// Store<AppState> store,
/// dynamic action,
/// NextDispatcher next,
/// ) {
/// next(action);
///
/// saveItems(store.state.items);
/// }
///
/// // We will then wire up specific types of actions to a List of Middleware
/// // that handle those actions.
/// final List<Middleware<AppState>> middleware = [
/// new TypedMiddleware<AppState, LoadTodosAction>(loadItemsMiddleware),
/// new TypedMiddleware<AppState, AddTodoAction>(saveItemsMiddleware),
/// new TypedMiddleware<AppState, ClearCompletedAction>(saveItemsMiddleware),
/// new TypedMiddleware<AppState, ToggleAllAction>(saveItemsMiddleware),
/// new TypedMiddleware<AppState, UpdateTodoAction>(saveItemsMiddleware),
/// new TypedMiddleware<AppState, TodosLoadedAction>(saveItemsMiddleware),
/// ];
/// ```
class TypedMiddleware<State, Action> implements MiddlewareClass<State> {
final void Function(
Store<State> store,
Action action,
NextDispatcher next,
) middleware;
TypedMiddleware(this.middleware);
@override
void call(Store<State> store, dynamic action, NextDispatcher next) {
if (action is Action) {
middleware(store, action, next);
} else {
next(action);
}
}
}
/// Defines a utility function that combines several reducers.
///
/// In order to prevent having one large, monolithic reducer in your app, it can
/// be convenient to break reducers up into smaller parts that handle more
/// specific functionality that can be decoupled and easily tested.
///
/// ### Example
///
/// helloReducer(state, action) {
/// return "hello";
/// }
///
/// friendReducer(state, action) {
/// return state + " friend";
/// }
///
/// final helloFriendReducer = combineReducers(
/// helloReducer,
/// friendReducer,
/// );
Reducer<State> combineReducers<State>(Iterable<Reducer<State>> reducers) {
return (State state, dynamic action) {
for (final reducer in reducers) {
state = reducer(state, action);
}
return state;
};
}

View File

@@ -0,0 +1,471 @@
# Generated by pub
# See https://www.dartlang.org/tools/pub/glossary#lockfile
packages:
analyzer:
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "0.36.3"
archive:
dependency: transitive
description:
name: archive
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.1"
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
bazel_worker:
dependency: transitive
description:
name: bazel_worker
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.20"
build:
dependency: transitive
description:
name: build
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.4"
build_config:
dependency: transitive
description:
name: build_config
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.0"
build_daemon:
dependency: transitive
description:
name: build_daemon
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.0"
build_modules:
dependency: transitive
description:
name: build_modules
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.2"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
build_runner:
dependency: "direct dev"
description:
name: build_runner
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.0"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.5"
build_web_compilers:
dependency: "direct dev"
description:
name: build_web_compilers
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
built_collection:
dependency: transitive
description:
name: built_collection
url: "https://pub.dartlang.org"
source: hosted
version: "4.2.1"
built_value:
dependency: transitive
description:
name: built_value
url: "https://pub.dartlang.org"
source: hosted
version: "6.5.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.2"
code_builder:
dependency: transitive
description:
name: code_builder
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.0"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.14.11"
convert:
dependency: transitive
description:
name: convert
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.6"
csslib:
dependency: transitive
description:
name: csslib
url: "https://pub.dartlang.org"
source: hosted
version: "0.16.0"
dart_style:
dependency: transitive
description:
name: dart_style
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.7"
fixnum:
dependency: transitive
description:
name: fixnum
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.9"
flutter_web:
dependency: "direct main"
description:
path: "packages/flutter_web"
ref: HEAD
resolved-ref: "7a92f7391ee8a72c398f879e357380084e2076b4"
url: "https://github.com/flutter/flutter_web"
source: git
version: "0.0.0"
flutter_web_ui:
dependency: "direct overridden"
description:
path: "packages/flutter_web_ui"
ref: HEAD
resolved-ref: "7a92f7391ee8a72c398f879e357380084e2076b4"
url: "https://github.com/flutter/flutter_web"
source: git
version: "0.0.0"
front_end:
dependency: transitive
description:
name: front_end
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.18"
glob:
dependency: transitive
description:
name: glob
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.7"
graphs:
dependency: transitive
description:
name: graphs
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
html:
dependency: transitive
description:
name: html
url: "https://pub.dartlang.org"
source: hosted
version: "0.14.0+2"
http:
dependency: transitive
description:
name: http
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.0+2"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.6"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.3"
intl:
dependency: transitive
description:
name: intl
url: "https://pub.dartlang.org"
source: hosted
version: "0.15.8"
io:
dependency: transitive
description:
name: io
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.3"
js:
dependency: transitive
description:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.1+1"
json_annotation:
dependency: transitive
description:
name: json_annotation
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
kernel:
dependency: transitive
description:
name: kernel
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.18"
logging:
dependency: transitive
description:
name: logging
url: "https://pub.dartlang.org"
source: hosted
version: "0.11.3+2"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.5"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.7"
mime:
dependency: transitive
description:
name: mime
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.6+2"
package_config:
dependency: transitive
description:
name: package_config
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.5"
package_resolver:
dependency: transitive
description:
name: package_resolver
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.10"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.2"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.0"
pool:
dependency: transitive
description:
name: pool
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.0"
protobuf:
dependency: transitive
description:
name: protobuf
url: "https://pub.dartlang.org"
source: hosted
version: "0.13.11"
pub_semver:
dependency: transitive
description:
name: pub_semver
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.2"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
quiver:
dependency: transitive
description:
name: quiver
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
scratch_space:
dependency: transitive
description:
name: scratch_space
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.3+2"
shelf:
dependency: transitive
description:
name: shelf
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.5"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.3"
source_maps:
dependency: transitive
description:
name: source_maps
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.8"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.5"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.9.3"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
stream_transform:
dependency: transitive
description:
name: stream_transform
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.19"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
timing:
dependency: transitive
description:
name: timing
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.1+1"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.6"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8"
watcher:
dependency: transitive
description:
name: watcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.7+10"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.12"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.15"
sdks:
dart: ">=2.3.0-dev.0.1 <3.0.0"

View File

@@ -0,0 +1,24 @@
name: vision_challenge
author: Yukkei Choi
environment:
sdk: ">=2.2.0 <3.0.0"
dependencies:
flutter_web: any
dev_dependencies:
build_runner: any
build_web_compilers: any
# flutter_web packages are not published to pub.dartlang.org
# These overrides tell the package tools to get them from GitHub
dependency_overrides:
flutter_web:
git:
url: https://github.com/flutter/flutter_web
path: packages/flutter_web
flutter_web_ui:
git:
url: https://github.com/flutter/flutter_web
path: packages/flutter_web_ui

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

View File

@@ -0,0 +1,10 @@
[
{
"family": "MaterialIcons",
"fonts": [
{
"asset": "https://fonts.gstatic.com/s/materialicons/v42/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2"
}
]
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 703 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<script defer src="main.dart.js" type="application/javascript"></script>
</head>
<body>
</body>
</html>

View File

@@ -0,0 +1,10 @@
// Copyright 2019 The Chromium 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:flutter_web_ui/ui.dart' as ui;
import 'package:vision_challenge/main.dart' as app;
main() async {
await ui.webOnlyInitializePlatform();
app.main();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB