mirror of
https://github.com/flutter/samples.git
synced 2026-03-26 22:31:45 +00:00
Add flutter_web samples (#75)
This commit is contained in:
committed by
Andrew Brogdon
parent
42f2dce01b
commit
3fe927cb29
13
web/timeflow/LICENSE
Normal file
13
web/timeflow/LICENSE
Normal file
@@ -0,0 +1,13 @@
|
||||
Copyright 2019 Fabian Stein
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
101
web/timeflow/README.md
Normal file
101
web/timeflow/README.md
Normal file
@@ -0,0 +1,101 @@
|
||||
A gentle animation that provides a calming experience to stressed developers.
|
||||
|
||||
Contributed as part of the Flutter Create 5K challenge by Fabian Stein.
|
||||
|
||||
Timers and stopwatches aren’t the most peaceful gadgets, often reminding us of
|
||||
urgent tasks, deadlines and unpleasant appointments. Not in this case, Timeflow
|
||||
is the epitome of pure tranquility, ideal for mindful activities: mediation,
|
||||
yoga or exercise. The slow, breath like animation is free of sudden, abrupt
|
||||
jumps and builds up to a Zen finish.
|
||||
|
||||
## Use
|
||||
|
||||
Tap the screen to start/pause the timer
|
||||
|
||||
when paused:
|
||||
|
||||
1. red button reset the timer and the animation
|
||||
|
||||
2. green button: resume the timer
|
||||
|
||||
when finished/startscreen:
|
||||
|
||||
1. blue button choose the desired timeframe
|
||||
|
||||
2. orange button randomize a new triangle mesh/color scheme
|
||||
|
||||
## Code description
|
||||
|
||||
Please run dartfmt for readability.
|
||||
|
||||
Some of the variable names are short and I have not used comments, because of the character limit, so here is an explanation.
|
||||
|
||||
### globals
|
||||
|
||||
triangles: the list of triangles that are animated
|
||||
|
||||
percent: how much of the timer is completed (from 0.0 to 1.0)
|
||||
|
||||
cTime: the time that is already gone by since the start of the timer (paused time is excluded)
|
||||
|
||||
dur: how long is the timer in Milliseconds
|
||||
|
||||
rng: the random number generator that is used throughout the program
|
||||
|
||||
rebuild: is an indicator that the triangles destination points should be rebuild
|
||||
|
||||
### class TM
|
||||
The timer class that manages the state of the app
|
||||
|
||||
SI cState: tracks the change of the apps state: is the timer stopped, playing or paused
|
||||
pTime: tracks when the ticker was paused
|
||||
Ticker t: the ticker that calls the update function up every frame
|
||||
up: function that updates the current time or stops the timer, when the duration is reached
|
||||
|
||||
press, pause, play, stop: callback functions, for the button presses
|
||||
openDialog: callback function, opens the numberPickerDialog, which is used to pick the timer duration
|
||||
build: returns the app, mainly the custom painter P is called
|
||||
|
||||
### class P
|
||||
The custom painter, which draws the triangles
|
||||
|
||||
paint:
|
||||
d = diameter of the circle is 2/3 of the width of the screen
|
||||
1. if the triangles are not setup completely (rebuild == true) calculate the outer points of for every triangle setupdP this happens here, because the ratio of the screens has got be known
|
||||
2. paint all triangles
|
||||
shouldRepaint: every frame should be repainted
|
||||
|
||||
### class T
|
||||
The triangle class
|
||||
|
||||
sP: the list of the starting points of the triangle (these are the points you see at the start of the animation)
|
||||
dP: the list of destination points (the outer points, where the triangles wander to first, before they circle back to the starting point)
|
||||
|
||||
constructor: p1,p2,p3 are the starting points, c is the overall color scheme (blue, red, green etc.)
|
||||
for the triangle a random color out of the color scheme is chosen: p.color = c[100 * (rng.nextInt(9) + 1)];
|
||||
the rest of the function determines, if the triangle is in the circle, if it is, it is added to the triangles list, otherwise it is forgotten and should be freed by the garbage collector
|
||||
|
||||
setupdP: setup the destination points, choose a random x and y position on the screen
|
||||
|
||||
cP: gives back the current points of the triangle with respect to the timerstate, some trigonometry and interpolations happen here
|
||||
this is responsible for the animations
|
||||
1. alter the alpha repetitively:
|
||||
2. alter the distance to the starting points, use a linear interpolation between the starting points sP and the destination points dP with respect to the percentage already done
|
||||
3. alter the angle with respect to the starting points
|
||||
4. alter the size of the triangles repetitively
|
||||
|
||||
### function setupT
|
||||
setup the Triangles (starting positions + color scheme)
|
||||
|
||||
dim: dimensions of the “net”
|
||||
1. make a net of points in the following manner:
|
||||
. . . . .
|
||||
. . . .
|
||||
. . . . .
|
||||
. . . .
|
||||
. . . . .
|
||||
. . . .
|
||||
2. alter the points a little bit by randomization, so that the net is a little more intresting
|
||||
3. connect the points to form triangles
|
||||
4. randomize a color scheme for the triangles
|
||||
|
||||
264
web/timeflow/lib/infinite_listview.dart
Normal file
264
web/timeflow/lib/infinite_listview.dart
Normal file
@@ -0,0 +1,264 @@
|
||||
// Package infinite_listview:
|
||||
// https://pub.dartlang.org/packages/infinite_listview
|
||||
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter_web/rendering.dart';
|
||||
import 'package:flutter_web/widgets.dart';
|
||||
|
||||
/// Infinite ListView
|
||||
///
|
||||
/// ListView that builds its children with to an infinite extent.
|
||||
///
|
||||
class InfiniteListView extends StatelessWidget {
|
||||
/// See [ListView.builder]
|
||||
InfiniteListView.builder({
|
||||
Key key,
|
||||
this.scrollDirection = Axis.vertical,
|
||||
this.reverse = false,
|
||||
InfiniteScrollController controller,
|
||||
this.physics,
|
||||
this.padding,
|
||||
this.itemExtent,
|
||||
@required IndexedWidgetBuilder itemBuilder,
|
||||
int itemCount,
|
||||
bool addAutomaticKeepAlives = true,
|
||||
bool addRepaintBoundaries = true,
|
||||
this.cacheExtent,
|
||||
}) : positiveChildrenDelegate = SliverChildBuilderDelegate(
|
||||
itemBuilder,
|
||||
childCount: itemCount,
|
||||
addAutomaticKeepAlives: addAutomaticKeepAlives,
|
||||
addRepaintBoundaries: addRepaintBoundaries,
|
||||
),
|
||||
negativeChildrenDelegate = SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) => itemBuilder(context, -1 - index),
|
||||
childCount: itemCount,
|
||||
addAutomaticKeepAlives: addAutomaticKeepAlives,
|
||||
addRepaintBoundaries: addRepaintBoundaries,
|
||||
),
|
||||
controller = controller ?? InfiniteScrollController(),
|
||||
super(key: key);
|
||||
|
||||
/// See [ListView.separated]
|
||||
InfiniteListView.separated({
|
||||
Key key,
|
||||
this.scrollDirection = Axis.vertical,
|
||||
this.reverse = false,
|
||||
InfiniteScrollController controller,
|
||||
this.physics,
|
||||
this.padding,
|
||||
@required IndexedWidgetBuilder itemBuilder,
|
||||
@required IndexedWidgetBuilder separatorBuilder,
|
||||
int itemCount,
|
||||
bool addAutomaticKeepAlives = true,
|
||||
bool addRepaintBoundaries = true,
|
||||
this.cacheExtent,
|
||||
}) : assert(itemBuilder != null),
|
||||
assert(separatorBuilder != null),
|
||||
itemExtent = null,
|
||||
positiveChildrenDelegate = SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
final itemIndex = index ~/ 2;
|
||||
return index.isEven
|
||||
? itemBuilder(context, itemIndex)
|
||||
: separatorBuilder(context, itemIndex);
|
||||
},
|
||||
childCount: itemCount != null ? math.max(0, itemCount * 2 - 1) : null,
|
||||
addAutomaticKeepAlives: addAutomaticKeepAlives,
|
||||
addRepaintBoundaries: addRepaintBoundaries,
|
||||
),
|
||||
negativeChildrenDelegate = SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
final itemIndex = (-1 - index) ~/ 2;
|
||||
return index.isOdd
|
||||
? itemBuilder(context, itemIndex)
|
||||
: separatorBuilder(context, itemIndex);
|
||||
},
|
||||
childCount: itemCount,
|
||||
addAutomaticKeepAlives: addAutomaticKeepAlives,
|
||||
addRepaintBoundaries: addRepaintBoundaries,
|
||||
),
|
||||
controller = controller ?? InfiniteScrollController(),
|
||||
super(key: key);
|
||||
|
||||
/// See: [ScrollView.scrollDirection]
|
||||
final Axis scrollDirection;
|
||||
|
||||
/// See: [ScrollView.reverse]
|
||||
final bool reverse;
|
||||
|
||||
/// See: [ScrollView.controller]
|
||||
final InfiniteScrollController controller;
|
||||
|
||||
/// See: [ScrollView.physics]
|
||||
final ScrollPhysics physics;
|
||||
|
||||
/// See: [BoxScrollView.padding]
|
||||
final EdgeInsets padding;
|
||||
|
||||
/// See: [ListView.itemExtent]
|
||||
final double itemExtent;
|
||||
|
||||
/// See: [ScrollView.cacheExtent]
|
||||
final double cacheExtent;
|
||||
|
||||
/// See: [ListView.childrenDelegate]
|
||||
final SliverChildDelegate negativeChildrenDelegate;
|
||||
|
||||
/// See: [ListView.childrenDelegate]
|
||||
final SliverChildDelegate positiveChildrenDelegate;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final List<Widget> slivers = _buildSlivers(context, negative: false);
|
||||
final List<Widget> negativeSlivers = _buildSlivers(context, negative: true);
|
||||
final AxisDirection axisDirection = _getDirection(context);
|
||||
final scrollPhysics = AlwaysScrollableScrollPhysics(parent: physics);
|
||||
return Scrollable(
|
||||
axisDirection: axisDirection,
|
||||
controller: controller,
|
||||
physics: scrollPhysics,
|
||||
viewportBuilder: (BuildContext context, ViewportOffset offset) {
|
||||
return Builder(builder: (BuildContext context) {
|
||||
/// Build negative [ScrollPosition] for the negative scrolling [Viewport].
|
||||
final state = Scrollable.of(context);
|
||||
final negativeOffset = _InfiniteScrollPosition(
|
||||
physics: scrollPhysics,
|
||||
context: state,
|
||||
initialPixels: -offset.pixels,
|
||||
keepScrollOffset: controller.keepScrollOffset,
|
||||
);
|
||||
|
||||
/// Keep the negative scrolling [Viewport] positioned to the [ScrollPosition].
|
||||
offset.addListener(() {
|
||||
negativeOffset._forceNegativePixels(offset.pixels);
|
||||
});
|
||||
|
||||
/// Stack the two [Viewport]s on top of each other so they move in sync.
|
||||
return Stack(
|
||||
children: <Widget>[
|
||||
Viewport(
|
||||
axisDirection: flipAxisDirection(axisDirection),
|
||||
anchor: 1.0,
|
||||
offset: negativeOffset,
|
||||
slivers: negativeSlivers,
|
||||
cacheExtent: cacheExtent,
|
||||
),
|
||||
Viewport(
|
||||
axisDirection: axisDirection,
|
||||
offset: offset,
|
||||
slivers: slivers,
|
||||
cacheExtent: cacheExtent,
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
AxisDirection _getDirection(BuildContext context) {
|
||||
return getAxisDirectionFromAxisReverseAndDirectionality(
|
||||
context, scrollDirection, reverse);
|
||||
}
|
||||
|
||||
List<Widget> _buildSlivers(BuildContext context, {bool negative = false}) {
|
||||
Widget sliver;
|
||||
if (itemExtent != null) {
|
||||
sliver = SliverFixedExtentList(
|
||||
delegate:
|
||||
negative ? negativeChildrenDelegate : positiveChildrenDelegate,
|
||||
itemExtent: itemExtent,
|
||||
);
|
||||
} else {
|
||||
sliver = SliverList(
|
||||
delegate:
|
||||
negative ? negativeChildrenDelegate : positiveChildrenDelegate);
|
||||
}
|
||||
if (padding != null) {
|
||||
sliver = new SliverPadding(
|
||||
padding: negative
|
||||
? padding - EdgeInsets.only(bottom: padding.bottom)
|
||||
: padding - EdgeInsets.only(top: padding.top),
|
||||
sliver: sliver,
|
||||
);
|
||||
}
|
||||
return <Widget>[sliver];
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties.add(new EnumProperty<Axis>('scrollDirection', scrollDirection));
|
||||
properties.add(new FlagProperty('reverse',
|
||||
value: reverse, ifTrue: 'reversed', showName: true));
|
||||
properties.add(new DiagnosticsProperty<ScrollController>(
|
||||
'controller', controller,
|
||||
showName: false, defaultValue: null));
|
||||
properties.add(new DiagnosticsProperty<ScrollPhysics>('physics', physics,
|
||||
showName: false, defaultValue: null));
|
||||
properties.add(new DiagnosticsProperty<EdgeInsetsGeometry>(
|
||||
'padding', padding,
|
||||
defaultValue: null));
|
||||
properties
|
||||
.add(new DoubleProperty('itemExtent', itemExtent, defaultValue: null));
|
||||
properties.add(
|
||||
new DoubleProperty('cacheExtent', cacheExtent, defaultValue: null));
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as a [ScrollController] except it provides [ScrollPosition] objects with infinite bounds.
|
||||
class InfiniteScrollController extends ScrollController {
|
||||
/// Creates a new [InfiniteScrollController]
|
||||
InfiniteScrollController({
|
||||
double initialScrollOffset = 0.0,
|
||||
bool keepScrollOffset = true,
|
||||
String debugLabel,
|
||||
}) : super(
|
||||
initialScrollOffset: initialScrollOffset,
|
||||
keepScrollOffset: keepScrollOffset,
|
||||
debugLabel: debugLabel,
|
||||
);
|
||||
|
||||
@override
|
||||
ScrollPosition createScrollPosition(ScrollPhysics physics,
|
||||
ScrollContext context, ScrollPosition oldPosition) {
|
||||
return new _InfiniteScrollPosition(
|
||||
physics: physics,
|
||||
context: context,
|
||||
initialPixels: initialScrollOffset,
|
||||
keepScrollOffset: keepScrollOffset,
|
||||
oldPosition: oldPosition,
|
||||
debugLabel: debugLabel,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _InfiniteScrollPosition extends ScrollPositionWithSingleContext {
|
||||
_InfiniteScrollPosition({
|
||||
@required ScrollPhysics physics,
|
||||
@required ScrollContext context,
|
||||
double initialPixels = 0.0,
|
||||
bool keepScrollOffset = true,
|
||||
ScrollPosition oldPosition,
|
||||
String debugLabel,
|
||||
}) : super(
|
||||
physics: physics,
|
||||
context: context,
|
||||
initialPixels: initialPixels,
|
||||
keepScrollOffset: keepScrollOffset,
|
||||
oldPosition: oldPosition,
|
||||
debugLabel: debugLabel,
|
||||
);
|
||||
|
||||
void _forceNegativePixels(double value) {
|
||||
super.forcePixels(-value);
|
||||
}
|
||||
|
||||
@override
|
||||
double get minScrollExtent => double.negativeInfinity;
|
||||
|
||||
@override
|
||||
double get maxScrollExtent => double.infinity;
|
||||
}
|
||||
260
web/timeflow/lib/main.dart
Normal file
260
web/timeflow/lib/main.dart
Normal file
@@ -0,0 +1,260 @@
|
||||
import 'dart:core';
|
||||
import 'dart:math';
|
||||
import 'package:flutter_web/material.dart';
|
||||
import 'package:flutter_web/scheduler.dart';
|
||||
|
||||
import 'numberpicker.dart';
|
||||
|
||||
main() => runApp(MaterialApp(home: App(), debugShowCheckedModeBanner: false));
|
||||
|
||||
class App extends StatefulWidget {
|
||||
@override
|
||||
State<StatefulWidget> createState() => TM();
|
||||
}
|
||||
|
||||
enum SI { pause, play, stop }
|
||||
List<T> triangles;
|
||||
var percent = 0.0, cTime = 0.0, dur = 120000.0, rng = Random(), rebuild = true;
|
||||
|
||||
class TM extends State<App> {
|
||||
SI cState = SI.stop;
|
||||
Ticker t;
|
||||
var pTime = 0.0;
|
||||
|
||||
@override
|
||||
initState() {
|
||||
// Screen.keepOn(true);
|
||||
t = Ticker(up);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
up(Duration d) {
|
||||
if (cState == SI.play) {
|
||||
setState(() {
|
||||
if (cTime >= dur)
|
||||
stop();
|
||||
else {
|
||||
cTime = d.inMilliseconds.toDouble() + pTime;
|
||||
percent = cTime / dur;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
press() {
|
||||
if (cState == SI.play)
|
||||
pause();
|
||||
else if (cState == SI.pause)
|
||||
play();
|
||||
else {
|
||||
cState = SI.play;
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
|
||||
pause() {
|
||||
setState(() {
|
||||
cState = SI.pause;
|
||||
t.stop();
|
||||
});
|
||||
}
|
||||
|
||||
play() {
|
||||
setState(() {
|
||||
cState = SI.play;
|
||||
t.start();
|
||||
pTime = cTime;
|
||||
});
|
||||
}
|
||||
|
||||
stop() {
|
||||
setState(() {
|
||||
cState = SI.stop;
|
||||
t.stop();
|
||||
pTime = 0.0;
|
||||
cTime = 0.0;
|
||||
percent = 0.0;
|
||||
});
|
||||
}
|
||||
|
||||
openDialog() {
|
||||
showDialog<num>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return NumberPickerDialog.integer(
|
||||
initialIntegerValue: (dur + 1.0) ~/ 60000,
|
||||
maxValue: 20,
|
||||
minValue: 1,
|
||||
title: Text('Minutes'));
|
||||
}).then((num v) {
|
||||
if (v != null) dur = 60000.0 * v;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<Widget> w = List();
|
||||
|
||||
if (cState == SI.pause) {
|
||||
w.add(fab(Colors.green, play, Icons.play_arrow));
|
||||
w.add(SizedBox(height: 10));
|
||||
w.add(fab(Colors.red, stop, Icons.close));
|
||||
w.add(SizedBox(height: 20));
|
||||
}
|
||||
|
||||
if (cState == SI.stop) {
|
||||
w.add(fab(Colors.lightBlue, openDialog, Icons.timer));
|
||||
w.add(SizedBox(height: 10));
|
||||
w.add(fab(Colors.yellow[900], () {
|
||||
rebuild = true;
|
||||
}, Icons.loop));
|
||||
w.add(SizedBox(height: 20));
|
||||
}
|
||||
|
||||
Column r = Column(mainAxisAlignment: MainAxisAlignment.end, children: w);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
body: SizedBox.expand(
|
||||
child: Container(
|
||||
child: CustomPaint(
|
||||
painter: P(),
|
||||
child: FlatButton(
|
||||
onPressed: press,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [r]))))));
|
||||
}
|
||||
}
|
||||
|
||||
FloatingActionButton fab(Color c, VoidCallback f, IconData ic) =>
|
||||
FloatingActionButton(backgroundColor: c, onPressed: f, child: Icon(ic));
|
||||
|
||||
class P extends CustomPainter {
|
||||
@override
|
||||
paint(Canvas canvas, Size size) {
|
||||
var w = size.width, h = size.height, d = 2 / 3 * w;
|
||||
if (w > 0.1 && h > 0.1) {
|
||||
if (rebuild) {
|
||||
rebuild = false;
|
||||
setupT();
|
||||
for (var t in triangles) t.setupdP(w / d, h / d);
|
||||
}
|
||||
|
||||
for (var t in triangles) {
|
||||
var cP = t.cP(), p = Path();
|
||||
p.moveTo(cP[0].x * d + w / 2, cP[0].y * d + h / 2);
|
||||
for (i = 1; i < 3; i++)
|
||||
p.lineTo(cP[i].x * d + w / 2, cP[i].y * d + h / 2);
|
||||
p.close();
|
||||
canvas.drawPath(p, t.p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(CustomPainter oldDelegate) => true;
|
||||
}
|
||||
|
||||
int i;
|
||||
|
||||
class T {
|
||||
List<Point> dP = List(3), sP = List(3);
|
||||
Paint p;
|
||||
|
||||
T(Point p1, p2, p3, var c) {
|
||||
p = Paint()..style = PaintingStyle.fill;
|
||||
sP[0] = p1;
|
||||
sP[1] = p2;
|
||||
sP[2] = p3;
|
||||
p.color = c[100 * (rng.nextInt(9) + 1)];
|
||||
|
||||
double x = 0, y = 0;
|
||||
for (i = 0; i < 3; i++) {
|
||||
x += sP[i].x;
|
||||
y += sP[i].y;
|
||||
}
|
||||
|
||||
x = 2 * x / 3;
|
||||
y = 2 * y / 3;
|
||||
if (x * x + y * y < 1) triangles.add(this);
|
||||
}
|
||||
|
||||
setupdP(double wR, hR) {
|
||||
var x = (rng.nextDouble() - 0.5) * (wR - 0.1),
|
||||
y = (rng.nextDouble() - 0.5) * (hR - 0.1);
|
||||
dP[0] = Point(x, y);
|
||||
for (i = 1; i < 3; i++)
|
||||
dP[i] = Point(sP[i].x + x - sP[0].x, sP[i].y + y - sP[0].y);
|
||||
}
|
||||
|
||||
List<Point> cP() {
|
||||
List<Point> res = List(3);
|
||||
var p, k, o = 6000, r;
|
||||
if (cTime < o)
|
||||
p = 1 - cTime / o;
|
||||
else
|
||||
p = (cTime - o) / (dur - o);
|
||||
k = 2 * ((cTime.toInt() % o) - o / 2).abs() / o;
|
||||
r = min(min(1, (dur - cTime) / o), cTime / o);
|
||||
this.p.color = this.p.color.withAlpha(255 - (200 * k * r).toInt());
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
res[i] = Point(
|
||||
sP[i].x * p + dP[i].x * (1 - p), sP[i].y * p + dP[i].y * (1 - p));
|
||||
|
||||
if (cTime > o) {
|
||||
var d = res[0].distanceTo(sP[0]);
|
||||
var a = acos((sP[0].x - res[0].x) / d);
|
||||
if (sP[0].y > res[0].y) a = 2 * pi - a;
|
||||
var b = pi - a + p * pi * dur / 120000;
|
||||
var dX = cos(b) * d, dY = sin(b) * d;
|
||||
for (i = 0; i < 3; i++) res[i] = Point(sP[i].x + dX, sP[i].y + dY);
|
||||
}
|
||||
|
||||
double mx = 0, my = 0;
|
||||
for (i = 0; i < 3; i++) {
|
||||
mx += res[i].x;
|
||||
my += res[i].y;
|
||||
}
|
||||
mx /= 3;
|
||||
my /= 3;
|
||||
for (i = 0; i < 3; i++)
|
||||
res[i] = Point(res[i].x + (res[i].x - mx) * (1 - k) * r / 2,
|
||||
res[i].y + (res[i].y - my) * (1 - k) * r / 2);
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
setupT() {
|
||||
int dim = 20, x, y;
|
||||
List<Point> tri = List(dim * dim);
|
||||
|
||||
for (x = 0; x < dim; x++) {
|
||||
for (y = 0; y < dim; y++) {
|
||||
var dx = rng.nextDouble() - 0.5, dy = rng.nextDouble() - 0.5, off;
|
||||
if (x % 2 == 0)
|
||||
off = 0;
|
||||
else
|
||||
off = 0.5;
|
||||
tri[x * dim + y] =
|
||||
Point((x + dx) / (dim - 1) - 0.5, (y + off + dy) / (dim - 1) - 0.5);
|
||||
}
|
||||
}
|
||||
triangles = List();
|
||||
var r = rng.nextInt(5), c;
|
||||
if (r == 0) c = Colors.lightBlue;
|
||||
if (r == 1) c = Colors.yellow;
|
||||
if (r == 2) c = Colors.lightGreen;
|
||||
if (r == 3) c = Colors.red;
|
||||
if (r == 4) c = Colors.pink;
|
||||
|
||||
for (x = 0; x < dim - 1; x++) {
|
||||
for (y = 0; y < dim - 1; y++) {
|
||||
int off = x * dim;
|
||||
T(tri[y + off], tri[y + 1 + off], tri[y + off + dim], c);
|
||||
T(tri[y + off + dim], tri[y + 1 + off], tri[y + 1 + off + dim], c);
|
||||
}
|
||||
}
|
||||
}
|
||||
527
web/timeflow/lib/numberpicker.dart
Normal file
527
web/timeflow/lib/numberpicker.dart
Normal file
@@ -0,0 +1,527 @@
|
||||
// Package numberpicker:
|
||||
// https://pub.dartlang.org/packages/numberpicker
|
||||
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter_web/foundation.dart';
|
||||
import 'package:flutter_web/material.dart';
|
||||
import 'package:flutter_web/rendering.dart';
|
||||
|
||||
import 'infinite_listview.dart';
|
||||
|
||||
/// Created by Marcin Szałek
|
||||
|
||||
///NumberPicker is a widget designed to pick a number between #minValue and #maxValue
|
||||
class NumberPicker extends StatelessWidget {
|
||||
///height of every list element
|
||||
static const double DEFAULT_ITEM_EXTENT = 50.0;
|
||||
|
||||
///width of list view
|
||||
static const double DEFAULT_LISTVIEW_WIDTH = 100.0;
|
||||
|
||||
///constructor for integer number picker
|
||||
NumberPicker.integer({
|
||||
Key key,
|
||||
@required int initialValue,
|
||||
@required this.minValue,
|
||||
@required this.maxValue,
|
||||
@required this.onChanged,
|
||||
this.itemExtent = DEFAULT_ITEM_EXTENT,
|
||||
this.listViewWidth = DEFAULT_LISTVIEW_WIDTH,
|
||||
this.step = 1,
|
||||
this.infiniteLoop = false,
|
||||
}) : assert(initialValue != null),
|
||||
assert(minValue != null),
|
||||
assert(maxValue != null),
|
||||
assert(maxValue > minValue),
|
||||
assert(initialValue >= minValue && initialValue <= maxValue),
|
||||
assert(step > 0),
|
||||
selectedIntValue = initialValue,
|
||||
selectedDecimalValue = -1,
|
||||
decimalPlaces = 0,
|
||||
intScrollController = infiniteLoop
|
||||
? new InfiniteScrollController(
|
||||
initialScrollOffset:
|
||||
(initialValue - minValue) ~/ step * itemExtent,
|
||||
)
|
||||
: new ScrollController(
|
||||
initialScrollOffset:
|
||||
(initialValue - minValue) ~/ step * itemExtent,
|
||||
),
|
||||
decimalScrollController = null,
|
||||
_listViewHeight = 3 * itemExtent,
|
||||
integerItemCount = (maxValue - minValue) ~/ step + 1,
|
||||
super(key: key);
|
||||
|
||||
///constructor for decimal number picker
|
||||
NumberPicker.decimal({
|
||||
Key key,
|
||||
@required double initialValue,
|
||||
@required this.minValue,
|
||||
@required this.maxValue,
|
||||
@required this.onChanged,
|
||||
this.decimalPlaces = 1,
|
||||
this.itemExtent = DEFAULT_ITEM_EXTENT,
|
||||
this.listViewWidth = DEFAULT_LISTVIEW_WIDTH,
|
||||
}) : assert(initialValue != null),
|
||||
assert(minValue != null),
|
||||
assert(maxValue != null),
|
||||
assert(decimalPlaces != null && decimalPlaces > 0),
|
||||
assert(maxValue > minValue),
|
||||
assert(initialValue >= minValue && initialValue <= maxValue),
|
||||
selectedIntValue = initialValue.floor(),
|
||||
selectedDecimalValue = ((initialValue - initialValue.floorToDouble()) *
|
||||
math.pow(10, decimalPlaces))
|
||||
.round(),
|
||||
intScrollController = new ScrollController(
|
||||
initialScrollOffset: (initialValue.floor() - minValue) * itemExtent,
|
||||
),
|
||||
decimalScrollController = new ScrollController(
|
||||
initialScrollOffset: ((initialValue - initialValue.floorToDouble()) *
|
||||
math.pow(10, decimalPlaces))
|
||||
.roundToDouble() *
|
||||
itemExtent,
|
||||
),
|
||||
_listViewHeight = 3 * itemExtent,
|
||||
step = 1,
|
||||
integerItemCount = maxValue.floor() - minValue.floor() + 1,
|
||||
infiniteLoop = false,
|
||||
super(key: key);
|
||||
|
||||
///called when selected value changes
|
||||
final ValueChanged<num> onChanged;
|
||||
|
||||
///min value user can pick
|
||||
final int minValue;
|
||||
|
||||
///max value user can pick
|
||||
final int maxValue;
|
||||
|
||||
///inidcates how many decimal places to show
|
||||
/// e.g. 0=>[1,2,3...], 1=>[1.0, 1.1, 1.2...] 2=>[1.00, 1.01, 1.02...]
|
||||
final int decimalPlaces;
|
||||
|
||||
///height of every list element in pixels
|
||||
final double itemExtent;
|
||||
|
||||
///view will always contain only 3 elements of list in pixels
|
||||
final double _listViewHeight;
|
||||
|
||||
///width of list view in pixels
|
||||
final double listViewWidth;
|
||||
|
||||
///ScrollController used for integer list
|
||||
final ScrollController intScrollController;
|
||||
|
||||
///ScrollController used for decimal list
|
||||
final ScrollController decimalScrollController;
|
||||
|
||||
///Currently selected integer value
|
||||
final int selectedIntValue;
|
||||
|
||||
///Currently selected decimal value
|
||||
final int selectedDecimalValue;
|
||||
|
||||
///Step between elements. Only for integer datePicker
|
||||
///Examples:
|
||||
/// if step is 100 the following elements may be 100, 200, 300...
|
||||
/// if min=0, max=6, step=3, then items will be 0, 3 and 6
|
||||
/// if min=0, max=5, step=3, then items will be 0 and 3.
|
||||
final int step;
|
||||
|
||||
///Repeat values infinitely
|
||||
final bool infiniteLoop;
|
||||
|
||||
///Amount of items
|
||||
final int integerItemCount;
|
||||
|
||||
//
|
||||
//----------------------------- PUBLIC ------------------------------
|
||||
//
|
||||
|
||||
animateInt(int valueToSelect) {
|
||||
int diff = valueToSelect - minValue;
|
||||
int index = diff ~/ step;
|
||||
animateIntToIndex(index);
|
||||
}
|
||||
|
||||
animateIntToIndex(int index) {
|
||||
_animate(intScrollController, index * itemExtent);
|
||||
}
|
||||
|
||||
animateDecimal(int decimalValue) {
|
||||
_animate(decimalScrollController, decimalValue * itemExtent);
|
||||
}
|
||||
|
||||
animateDecimalAndInteger(double valueToSelect) {
|
||||
animateInt(valueToSelect.floor());
|
||||
animateDecimal(((valueToSelect - valueToSelect.floorToDouble()) *
|
||||
math.pow(10, decimalPlaces))
|
||||
.round());
|
||||
}
|
||||
|
||||
//
|
||||
//----------------------------- VIEWS -----------------------------
|
||||
//
|
||||
|
||||
///main widget
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ThemeData themeData = Theme.of(context);
|
||||
|
||||
if (infiniteLoop) {
|
||||
return _integerInfiniteListView(themeData);
|
||||
}
|
||||
if (decimalPlaces == 0) {
|
||||
return _integerListView(themeData);
|
||||
} else {
|
||||
return new Row(
|
||||
children: <Widget>[
|
||||
_integerListView(themeData),
|
||||
_decimalListView(themeData),
|
||||
],
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _integerListView(ThemeData themeData) {
|
||||
TextStyle defaultStyle = themeData.textTheme.body1;
|
||||
TextStyle selectedStyle =
|
||||
themeData.textTheme.headline.copyWith(color: themeData.accentColor);
|
||||
|
||||
var listItemCount = integerItemCount + 2;
|
||||
|
||||
return new NotificationListener(
|
||||
child: new Container(
|
||||
height: _listViewHeight,
|
||||
width: listViewWidth,
|
||||
child: new ListView.builder(
|
||||
controller: intScrollController,
|
||||
itemExtent: itemExtent,
|
||||
itemCount: listItemCount,
|
||||
cacheExtent: _calculateCacheExtent(listItemCount),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final int value = _intValueFromIndex(index);
|
||||
|
||||
//define special style for selected (middle) element
|
||||
final TextStyle itemStyle =
|
||||
value == selectedIntValue ? selectedStyle : defaultStyle;
|
||||
|
||||
bool isExtra = index == 0 || index == listItemCount - 1;
|
||||
|
||||
return isExtra
|
||||
? new Container() //empty first and last element
|
||||
: new Center(
|
||||
child: new Text(value.toString(), style: itemStyle),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
onNotification: _onIntegerNotification,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _decimalListView(ThemeData themeData) {
|
||||
TextStyle defaultStyle = themeData.textTheme.body1;
|
||||
TextStyle selectedStyle =
|
||||
themeData.textTheme.headline.copyWith(color: themeData.accentColor);
|
||||
|
||||
int decimalItemCount =
|
||||
selectedIntValue == maxValue ? 3 : math.pow(10, decimalPlaces) + 2;
|
||||
|
||||
return new NotificationListener(
|
||||
child: new Container(
|
||||
height: _listViewHeight,
|
||||
width: listViewWidth,
|
||||
child: new ListView.builder(
|
||||
controller: decimalScrollController,
|
||||
itemExtent: itemExtent,
|
||||
itemCount: decimalItemCount,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final int value = index - 1;
|
||||
|
||||
//define special style for selected (middle) element
|
||||
final TextStyle itemStyle =
|
||||
value == selectedDecimalValue ? selectedStyle : defaultStyle;
|
||||
|
||||
bool isExtra = index == 0 || index == decimalItemCount - 1;
|
||||
|
||||
return isExtra
|
||||
? new Container() //empty first and last element
|
||||
: new Center(
|
||||
child: new Text(
|
||||
value.toString().padLeft(decimalPlaces, '0'),
|
||||
style: itemStyle),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
onNotification: _onDecimalNotification,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _integerInfiniteListView(ThemeData themeData) {
|
||||
TextStyle defaultStyle = themeData.textTheme.body1;
|
||||
TextStyle selectedStyle =
|
||||
themeData.textTheme.headline.copyWith(color: themeData.accentColor);
|
||||
|
||||
return new NotificationListener(
|
||||
child: new Container(
|
||||
height: _listViewHeight,
|
||||
width: listViewWidth,
|
||||
child: new InfiniteListView.builder(
|
||||
controller: intScrollController,
|
||||
itemExtent: itemExtent,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final int value = _intValueFromIndex(index);
|
||||
|
||||
//define special style for selected (middle) element
|
||||
final TextStyle itemStyle =
|
||||
value == selectedIntValue ? selectedStyle : defaultStyle;
|
||||
|
||||
return new Center(
|
||||
child: new Text(value.toString(), style: itemStyle),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
onNotification: _onIntegerNotification,
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// ----------------------------- LOGIC -----------------------------
|
||||
//
|
||||
|
||||
int _intValueFromIndex(int index) {
|
||||
index--;
|
||||
index %= integerItemCount;
|
||||
return minValue + index * step;
|
||||
}
|
||||
|
||||
bool _onIntegerNotification(Notification notification) {
|
||||
if (notification is ScrollNotification) {
|
||||
//calculate
|
||||
int intIndexOfMiddleElement =
|
||||
(notification.metrics.pixels / itemExtent).round();
|
||||
if (!infiniteLoop) {
|
||||
intIndexOfMiddleElement =
|
||||
intIndexOfMiddleElement.clamp(0, integerItemCount - 1);
|
||||
}
|
||||
int intValueInTheMiddle = _intValueFromIndex(intIndexOfMiddleElement + 1);
|
||||
intValueInTheMiddle = _normalizeIntegerMiddleValue(intValueInTheMiddle);
|
||||
|
||||
if (_userStoppedScrolling(notification, intScrollController)) {
|
||||
//center selected value
|
||||
animateIntToIndex(intIndexOfMiddleElement);
|
||||
}
|
||||
|
||||
//update selection
|
||||
if (intValueInTheMiddle != selectedIntValue) {
|
||||
num newValue;
|
||||
if (decimalPlaces == 0) {
|
||||
//return integer value
|
||||
newValue = (intValueInTheMiddle);
|
||||
} else {
|
||||
if (intValueInTheMiddle == maxValue) {
|
||||
//if new value is maxValue, then return that value and ignore decimal
|
||||
newValue = (intValueInTheMiddle.toDouble());
|
||||
animateDecimal(0);
|
||||
} else {
|
||||
//return integer+decimal
|
||||
double decimalPart = _toDecimal(selectedDecimalValue);
|
||||
newValue = ((intValueInTheMiddle + decimalPart).toDouble());
|
||||
}
|
||||
}
|
||||
onChanged(newValue);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _onDecimalNotification(Notification notification) {
|
||||
if (notification is ScrollNotification) {
|
||||
//calculate middle value
|
||||
int indexOfMiddleElement =
|
||||
(notification.metrics.pixels + _listViewHeight / 2) ~/ itemExtent;
|
||||
int decimalValueInTheMiddle = indexOfMiddleElement - 1;
|
||||
decimalValueInTheMiddle =
|
||||
_normalizeDecimalMiddleValue(decimalValueInTheMiddle);
|
||||
|
||||
if (_userStoppedScrolling(notification, decimalScrollController)) {
|
||||
//center selected value
|
||||
animateDecimal(decimalValueInTheMiddle);
|
||||
}
|
||||
|
||||
//update selection
|
||||
if (selectedIntValue != maxValue &&
|
||||
decimalValueInTheMiddle != selectedDecimalValue) {
|
||||
double decimalPart = _toDecimal(decimalValueInTheMiddle);
|
||||
double newValue = ((selectedIntValue + decimalPart).toDouble());
|
||||
onChanged(newValue);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
///There was a bug, when if there was small integer range, e.g. from 1 to 5,
|
||||
///When user scrolled to the top, whole listview got displayed.
|
||||
///To prevent this we are calculating cacheExtent by our own so it gets smaller if number of items is smaller
|
||||
double _calculateCacheExtent(int itemCount) {
|
||||
double cacheExtent = 250.0; //default cache extent
|
||||
if ((itemCount - 2) * DEFAULT_ITEM_EXTENT <= cacheExtent) {
|
||||
cacheExtent = ((itemCount - 3) * DEFAULT_ITEM_EXTENT);
|
||||
}
|
||||
return cacheExtent;
|
||||
}
|
||||
|
||||
///When overscroll occurs on iOS,
|
||||
///we can end up with value not in the range between [minValue] and [maxValue]
|
||||
///To avoid going out of range, we change values out of range to border values.
|
||||
int _normalizeMiddleValue(int valueInTheMiddle, int min, int max) {
|
||||
return math.max(math.min(valueInTheMiddle, max), min);
|
||||
}
|
||||
|
||||
int _normalizeIntegerMiddleValue(int integerValueInTheMiddle) {
|
||||
//make sure that max is a multiple of step
|
||||
int max = (maxValue ~/ step) * step;
|
||||
return _normalizeMiddleValue(integerValueInTheMiddle, minValue, max);
|
||||
}
|
||||
|
||||
int _normalizeDecimalMiddleValue(int decimalValueInTheMiddle) {
|
||||
return _normalizeMiddleValue(
|
||||
decimalValueInTheMiddle, 0, math.pow(10, decimalPlaces) - 1);
|
||||
}
|
||||
|
||||
///indicates if user has stopped scrolling so we can center value in the middle
|
||||
bool _userStoppedScrolling(
|
||||
Notification notification, ScrollController scrollController) {
|
||||
return notification is UserScrollNotification &&
|
||||
notification.direction == ScrollDirection.idle &&
|
||||
// ignore: invalid_use_of_protected_member
|
||||
scrollController.position.activity is! HoldScrollActivity;
|
||||
}
|
||||
|
||||
///converts integer indicator of decimal value to double
|
||||
///e.g. decimalPlaces = 1, value = 4 >>> result = 0.4
|
||||
/// decimalPlaces = 2, value = 12 >>> result = 0.12
|
||||
double _toDecimal(int decimalValueAsInteger) {
|
||||
return double.parse((decimalValueAsInteger * math.pow(10, -decimalPlaces))
|
||||
.toStringAsFixed(decimalPlaces));
|
||||
}
|
||||
|
||||
///scroll to selected value
|
||||
_animate(ScrollController scrollController, double value) {
|
||||
scrollController.animateTo(value,
|
||||
duration: new Duration(seconds: 1), curve: new ElasticOutCurve());
|
||||
}
|
||||
}
|
||||
|
||||
///Returns AlertDialog as a Widget so it is designed to be used in showDialog method
|
||||
class NumberPickerDialog extends StatefulWidget {
|
||||
final int minValue;
|
||||
final int maxValue;
|
||||
final int initialIntegerValue;
|
||||
final double initialDoubleValue;
|
||||
final int decimalPlaces;
|
||||
final Widget title;
|
||||
final EdgeInsets titlePadding;
|
||||
final Widget confirmWidget;
|
||||
final Widget cancelWidget;
|
||||
final int step;
|
||||
final bool infiniteLoop;
|
||||
|
||||
///constructor for integer values
|
||||
NumberPickerDialog.integer({
|
||||
@required this.minValue,
|
||||
@required this.maxValue,
|
||||
@required this.initialIntegerValue,
|
||||
this.title,
|
||||
this.titlePadding,
|
||||
this.step = 1,
|
||||
this.infiniteLoop = false,
|
||||
Widget confirmWidget,
|
||||
Widget cancelWidget,
|
||||
}) : confirmWidget = confirmWidget ?? new Text("OK"),
|
||||
cancelWidget = cancelWidget ?? new Text("CANCEL"),
|
||||
decimalPlaces = 0,
|
||||
initialDoubleValue = -1.0;
|
||||
|
||||
///constructor for decimal values
|
||||
NumberPickerDialog.decimal({
|
||||
@required this.minValue,
|
||||
@required this.maxValue,
|
||||
@required this.initialDoubleValue,
|
||||
this.decimalPlaces = 1,
|
||||
this.title,
|
||||
this.titlePadding,
|
||||
Widget confirmWidget,
|
||||
Widget cancelWidget,
|
||||
}) : confirmWidget = confirmWidget ?? new Text("OK"),
|
||||
cancelWidget = cancelWidget ?? new Text("CANCEL"),
|
||||
initialIntegerValue = -1,
|
||||
step = 1,
|
||||
infiniteLoop = false;
|
||||
|
||||
@override
|
||||
State<NumberPickerDialog> createState() =>
|
||||
new _NumberPickerDialogControllerState(
|
||||
initialIntegerValue, initialDoubleValue);
|
||||
}
|
||||
|
||||
class _NumberPickerDialogControllerState extends State<NumberPickerDialog> {
|
||||
int selectedIntValue;
|
||||
double selectedDoubleValue;
|
||||
|
||||
_NumberPickerDialogControllerState(
|
||||
this.selectedIntValue, this.selectedDoubleValue);
|
||||
|
||||
_handleValueChanged(num value) {
|
||||
if (value is int) {
|
||||
setState(() => selectedIntValue = value);
|
||||
} else {
|
||||
setState(() => selectedDoubleValue = value);
|
||||
}
|
||||
}
|
||||
|
||||
NumberPicker _buildNumberPicker() {
|
||||
if (widget.decimalPlaces > 0) {
|
||||
return new NumberPicker.decimal(
|
||||
initialValue: selectedDoubleValue,
|
||||
minValue: widget.minValue,
|
||||
maxValue: widget.maxValue,
|
||||
decimalPlaces: widget.decimalPlaces,
|
||||
onChanged: _handleValueChanged);
|
||||
} else {
|
||||
return new NumberPicker.integer(
|
||||
initialValue: selectedIntValue,
|
||||
minValue: widget.minValue,
|
||||
maxValue: widget.maxValue,
|
||||
step: widget.step,
|
||||
infiniteLoop: widget.infiniteLoop,
|
||||
onChanged: _handleValueChanged,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new AlertDialog(
|
||||
title: widget.title,
|
||||
titlePadding: widget.titlePadding,
|
||||
content: _buildNumberPicker(),
|
||||
actions: [
|
||||
new FlatButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: widget.cancelWidget,
|
||||
),
|
||||
new FlatButton(
|
||||
onPressed: () => Navigator.of(context).pop(widget.decimalPlaces > 0
|
||||
? selectedDoubleValue
|
||||
: selectedIntValue),
|
||||
child: widget.confirmWidget),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
471
web/timeflow/pubspec.lock
Normal file
471
web/timeflow/pubspec.lock
Normal 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 main"
|
||||
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"
|
||||
23
web/timeflow/pubspec.yaml
Normal file
23
web/timeflow/pubspec.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
name: timeflow
|
||||
|
||||
environment:
|
||||
sdk: ">=2.2.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter_web: any
|
||||
flutter_web_ui: 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
|
||||
10
web/timeflow/web/assets/FontManifest.json
Normal file
10
web/timeflow/web/assets/FontManifest.json
Normal file
@@ -0,0 +1,10 @@
|
||||
[
|
||||
{
|
||||
"family": "MaterialIcons",
|
||||
"fonts": [
|
||||
{
|
||||
"asset": "https://fonts.gstatic.com/s/materialicons/v42/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
10
web/timeflow/web/index.html
Normal file
10
web/timeflow/web/index.html
Normal 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>
|
||||
10
web/timeflow/web/main.dart
Normal file
10
web/timeflow/web/main.dart
Normal 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:timeflow/main.dart' as app;
|
||||
|
||||
main() async {
|
||||
await ui.webOnlyInitializePlatform();
|
||||
app.main();
|
||||
}
|
||||
BIN
web/timeflow/web/preview.png
Normal file
BIN
web/timeflow/web/preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
Reference in New Issue
Block a user