mirror of https://github.com/flutter/samples.git
Add workflow to deploy the sample index (#791)
* Add workflow to build and deploy the sample index * update gh-pages action * fix yaml * create web/ directory in build * grammar * add ignored directories * revert pubspec.lock files * add job to run _tool/verify_samples.dart * Update filipino_cuisine for Flutter 2 * remove timeflow demo. The unnamed List constructor is now deprecated, refactoring this code to use add() requires more knowledge about the code for this demo. https://dart.dev/null-safety/understanding-null-safety#no-unnamed-list-constructor * update slide_puzzle * ensure stable channel is used to verify * move verify web demos action into separate yaml file - avoid running on each flutter version. * add on: pull_request * update slide_puzzle * Update gh-pages.yml * Add copyright headerpull/796/head
parent
b26f2cccc1
commit
3f5ab56485
@ -0,0 +1,31 @@
|
||||
name: Deploy to GitHub Pages
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: subosito/flutter-action@v1
|
||||
|
||||
- name: Init scripts
|
||||
run: dart pub get
|
||||
working-directory: web/_tool
|
||||
|
||||
- name: Build
|
||||
run: dart _tool/build_ci.dart
|
||||
working-directory: web
|
||||
|
||||
- name: Deploy
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: web/samples_index/public
|
@ -0,0 +1,20 @@
|
||||
name: Verify web demos
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
verify-web-demos:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- uses: subosito/flutter-action@v1
|
||||
with:
|
||||
channel: stable
|
||||
- name: Init scripts
|
||||
run: dart pub get
|
||||
working-directory: web/_tool
|
||||
- name: Verify packages
|
||||
run: dart _tool/verify_packages.dart
|
||||
working-directory: web
|
@ -0,0 +1,43 @@
|
||||
// Copyright 2021 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file
|
||||
|
||||
import 'dart:io';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'common.dart';
|
||||
|
||||
final ignoredDirectories = ['_tool', 'samples_index'];
|
||||
|
||||
main() async {
|
||||
final packageDirs = [
|
||||
...listPackageDirs(Directory.current)
|
||||
.map((path) => p.relative(path, from: Directory.current.path))
|
||||
.where((path) => !ignoredDirectories.contains(path))
|
||||
];
|
||||
|
||||
print('Building the sample index...');
|
||||
await run('samples_index', 'pub', ['get']);
|
||||
await run('samples_index', 'pub', ['run', 'grinder', 'deploy']);
|
||||
|
||||
// Create the directory each Flutter Web sample lives in
|
||||
Directory(p.join(Directory.current.path, 'samples_index', 'public', 'web'))
|
||||
.createSync(recursive: true);
|
||||
|
||||
for (var i = 0; i < packageDirs.length; i++) {
|
||||
var directory = packageDirs[i];
|
||||
|
||||
logWrapped(ansiMagenta, '\n$directory (${i + 1} of ${packageDirs.length})');
|
||||
|
||||
// Create the target directory
|
||||
var directoryName = p.basename(directory);
|
||||
var sourceBuildDir =
|
||||
p.join(Directory.current.path, directory, 'build', 'web');
|
||||
var targetDirectory = p.join(Directory.current.path, 'samples_index',
|
||||
'public', 'web', directoryName);
|
||||
|
||||
// Build the sample and copy the files
|
||||
await run(directory, 'flutter', ['pub', 'get']);
|
||||
await run(directory, 'flutter', ['build', 'web']);
|
||||
await run(directory, 'mv', [sourceBuildDir, targetDirectory]);
|
||||
}
|
||||
}
|
@ -1 +1 @@
|
||||
Subproject commit 5ef5526acb58f9ffb6f3fb22118cbd825613dc73
|
||||
Subproject commit 5c590d0b0252cf3d7cbddf9998d7807b87c91550
|
@ -1,13 +0,0 @@
|
||||
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.
|
Before Width: | Height: | Size: 40 KiB |
@ -1,264 +0,0 @@
|
||||
// Package infinite_listview:
|
||||
// https://pub.dartlang.org/packages/infinite_listview
|
||||
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/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;
|
||||
}
|
@ -1,260 +0,0 @@
|
||||
import 'dart:core';
|
||||
import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/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: TextButton(
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,527 +0,0 @@
|
||||
// Package numberpicker:
|
||||
// https://pub.dartlang.org/packages/numberpicker
|
||||
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/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.bodyText2;
|
||||
TextStyle selectedStyle =
|
||||
themeData.textTheme.headline5.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.bodyText2;
|
||||
TextStyle selectedStyle =
|
||||
themeData.textTheme.headline5.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.bodyText2;
|
||||
TextStyle selectedStyle =
|
||||
themeData.textTheme.headline5.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,invalid_use_of_visible_for_testing_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 TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: widget.cancelWidget,
|
||||
),
|
||||
new TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(widget.decimalPlaces > 0
|
||||
? selectedDoubleValue
|
||||
: selectedIntValue),
|
||||
child: widget.confirmWidget),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0-nullsafety.5"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.15.0-nullsafety.5"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0-nullsafety.6"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.99"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0-nullsafety.5"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0-nullsafety.5"
|
||||
sdks:
|
||||
dart: ">=2.12.0-0 <3.0.0"
|
@ -1,12 +0,0 @@
|
||||
name: timeflow
|
||||
|
||||
environment:
|
||||
sdk: ">=2.2.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
assets:
|
||||
- preview.png
|
@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title></title>
|
||||
<script defer src="main.dart.js" type="application/javascript"></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in new issue