diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml new file mode 100644 index 000000000..ad41133fc --- /dev/null +++ b/.github/workflows/gh-pages.yml @@ -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 diff --git a/.github/workflows/verify-web-demos.yml b/.github/workflows/verify-web-demos.yml new file mode 100644 index 000000000..c7aacf9a8 --- /dev/null +++ b/.github/workflows/verify-web-demos.yml @@ -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 diff --git a/web/_tool/build_ci.dart b/web/_tool/build_ci.dart new file mode 100644 index 000000000..d73f0e0c7 --- /dev/null +++ b/web/_tool/build_ci.dart @@ -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]); + } +} diff --git a/web/_tool/common.dart b/web/_tool/common.dart index 7a4adfc43..ae332c101 100644 --- a/web/_tool/common.dart +++ b/web/_tool/common.dart @@ -36,3 +36,16 @@ Future run( void logWrapped(int code, String message) { print('\x1B[${code}m$message\x1B[0m'); } + +Iterable listPackageDirs(Directory dir) sync* { + if (File('${dir.path}/pubspec.yaml').existsSync()) { + yield dir.path; + } else { + for (var subDir in dir + .listSync(followLinks: true) + .whereType() + .where((d) => !Uri.file(d.path).pathSegments.last.startsWith('.'))) { + yield* listPackageDirs(subDir); + } + } +} diff --git a/web/_tool/verify_packages.dart b/web/_tool/verify_packages.dart index 1530bd409..68294bd65 100644 --- a/web/_tool/verify_packages.dart +++ b/web/_tool/verify_packages.dart @@ -5,14 +5,11 @@ import 'dart:io'; import 'package:path/path.dart' as p; + import 'common.dart'; -const _ansiGreen = 32; -const _ansiRed = 31; -const _ansiMagenta = 35; - void main() async { - final packageDirs = _listPackageDirs(Directory.current) + final packageDirs = listPackageDirs(Directory.current) .map((path) => p.relative(path, from: Directory.current.path)) .toList(); @@ -21,7 +18,7 @@ void main() async { final results = []; for (var i = 0; i < packageDirs.length; i++) { final dir = packageDirs[i]; - logWrapped(_ansiMagenta, '\n$dir (${i + 1} of ${packageDirs.length})'); + logWrapped(ansiMagenta, '\n$dir (${i + 1} of ${packageDirs.length})'); results.add(await run(dir, 'flutter', [ 'pub', 'pub', @@ -46,19 +43,6 @@ void _printStatus(List results) { var success = (successCount == results.length); var pct = 100 * successCount / results.length; - logWrapped(success ? _ansiGreen : _ansiRed, + logWrapped(success ? ansiGreen : ansiRed, '$successCount of ${results.length} (${pct.toStringAsFixed(2)}%)'); } - -Iterable _listPackageDirs(Directory dir) sync* { - if (File('${dir.path}/pubspec.yaml').existsSync()) { - yield dir.path; - } else { - for (var subDir in dir - .listSync(followLinks: true) - .whereType() - .where((d) => !Uri.file(d.path).pathSegments.last.startsWith('.'))) { - yield* _listPackageDirs(subDir); - } - } -} diff --git a/web/filipino_cuisine/lib/cook.dart b/web/filipino_cuisine/lib/cook.dart index a1eeea1f6..e15e922a6 100644 --- a/web/filipino_cuisine/lib/cook.dart +++ b/web/filipino_cuisine/lib/cook.dart @@ -14,7 +14,7 @@ class CState extends State { initState() { super.initState(); - cb = List(); + cb = []; } Widget build(ct) { diff --git a/web/github_dataviz/lib/catmull.dart b/web/github_dataviz/lib/catmull.dart index a0a5fa7a9..a1b735763 100644 --- a/web/github_dataviz/lib/catmull.dart +++ b/web/github_dataviz/lib/catmull.dart @@ -60,7 +60,7 @@ class CatmullInterpolator implements Interpolator { } static void test() { - List controlPoints = List(); + List controlPoints = []; controlPoints.add(Point2D(-1, 1)); controlPoints.add(Point2D(0, 1)); controlPoints.add(Point2D(1, -1)); diff --git a/web/github_dataviz/lib/layered_chart.dart b/web/github_dataviz/lib/layered_chart.dart index c39f8bb12..41ecebde4 100644 --- a/web/github_dataviz/lib/layered_chart.dart +++ b/web/github_dataviz/lib/layered_chart.dart @@ -47,12 +47,12 @@ class LayeredChartState extends State { graphHeight = MathUtils.clampedMap(screenRatio, 0.5, 2.5, 50, 150); int m = dataToPlot.length; - paths = List(m); - capPaths = List(m); - maxValues = List(m); + paths = []; + capPaths = []; + maxValues = []; for (int i = 0; i < m; i++) { int n = dataToPlot[i].series.length; - maxValues[i] = 0; + maxValues.add(0); for (int j = 0; j < n; j++) { double v = dataToPlot[i].series[j].toDouble(); if (v > maxValues[i]) { @@ -69,11 +69,11 @@ class LayeredChartState extends State { double xWidth = (endX - startX) / numPoints; double capRangeX = capSize * cos(capTheta); double tanCapTheta = tan(capTheta); - List curvePoints = List(numPoints); + List curvePoints = []; for (int i = 0; i < m; i++) { List series = dataToPlot[i].series; int n = series.length; - List controlPoints = List(); + List controlPoints = []; controlPoints.add(Point2D(-1, 0)); double last = 0; for (int j = 0; j < n; j++) { @@ -88,11 +88,11 @@ class LayeredChartState extends State { cpv.value = MathUtils.map( j.toDouble(), 0, (numPoints - 1).toDouble(), 0, (n - 1).toDouble()); curve.progressiveGet(cpv); - curvePoints[j] = MathUtils.map( - max(0, cpv.value), 0, maxValues[i].toDouble(), 0, graphHeight); + curvePoints.add(MathUtils.map( + max(0, cpv.value), 0, maxValues[i].toDouble(), 0, graphHeight)); } - paths[i] = Path(); - capPaths[i] = Path(); + paths.add(Path()); + capPaths.add(Path()); paths[i].moveTo(startX, startY); capPaths[i].moveTo(startX, startY); for (int j = 0; j < numPoints; j++) { @@ -133,7 +133,7 @@ class LayeredChartState extends State { capPaths[i].lineTo(startX, startY + 1); capPaths[i].close(); } - labelPainter = List(); + labelPainter = []; for (int i = 0; i < dataToPlot.length; i++) { TextSpan span = TextSpan( style: TextStyle( @@ -146,7 +146,7 @@ class LayeredChartState extends State { tp.layout(); labelPainter.add(tp); } - milestonePainter = List(); + milestonePainter = []; for (int i = 0; i < milestones.length; i++) { TextSpan span = TextSpan( style: TextStyle( diff --git a/web/github_dataviz/lib/main.dart b/web/github_dataviz/lib/main.dart index 92dc260e3..747e23b73 100644 --- a/web/github_dataviz/lib/main.dart +++ b/web/github_dataviz/lib/main.dart @@ -46,7 +46,7 @@ class _MainLayoutState extends State with TickerProviderStateMixin { createAnimation(0); - weekLabels = List(); + weekLabels = []; weekLabels.add(WeekLabel.forDate(DateTime(2019, 2, 26), "v1.2")); weekLabels.add(WeekLabel.forDate(DateTime(2018, 12, 4), "v1.0")); // weekLabels.add(WeekLabel.forDate(new DateTime(2018, 9, 19), "Preview 2")); @@ -79,9 +79,9 @@ class _MainLayoutState extends State with TickerProviderStateMixin { @override Widget build(BuildContext context) { // Combined contributions data - List dataToPlot = List(); + List dataToPlot = []; if (contributions != null) { - List series = List(); + List series = []; for (UserContribution userContrib in contributions) { for (int i = 0; i < userContrib.contributions.length; i++) { ContributionData data = userContrib.contributions[i]; @@ -228,7 +228,7 @@ class _MainLayoutState extends State with TickerProviderStateMixin { List summarizeWeeksFromTSV( String statByWeekStr, int numWeeksTotal) { - List loadedStats = List(); + List loadedStats = []; HashMap statMap = HashMap(); statByWeekStr.split("\n").forEach((s) { List split = s.split("\t"); @@ -237,7 +237,8 @@ class _MainLayoutState extends State with TickerProviderStateMixin { statMap[weekNum] = StatForWeek(weekNum, int.parse(split[1])); } }); - print("Laoded ${statMap.length} weeks."); + print("Loaded ${statMap.length} weeks."); + // Convert into a list by week, but fill in empty weeks with 0 for (int i = 0; i < numWeeksTotal; i++) { StatForWeek starsForWeek = statMap[i]; diff --git a/web/github_dataviz/pubspec.lock b/web/github_dataviz/pubspec.lock index 056dbbceb..966a76c63 100644 --- a/web/github_dataviz/pubspec.lock +++ b/web/github_dataviz/pubspec.lock @@ -7,7 +7,7 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.5" + version: "1.1.0" charcode: dependency: transitive description: @@ -21,7 +21,7 @@ packages: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0-nullsafety.5" + version: "1.15.0" flutter: dependency: "direct main" description: flutter @@ -54,7 +54,7 @@ packages: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.6" + version: "1.3.0" path: dependency: transitive description: @@ -101,13 +101,13 @@ packages: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.5" + version: "1.3.0" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.5" + version: "2.1.0" sdks: dart: ">=2.12.0-0 <3.0.0" diff --git a/web/peanut.yaml b/web/peanut.yaml index 16440125e..557a34fce 100644 --- a/web/peanut.yaml +++ b/web/peanut.yaml @@ -8,7 +8,6 @@ directories: - github_dataviz/web - particle_background/web - slide_puzzle/web -- timeflow/web - form_app/web - web_dashboard/web - place_tracker/web diff --git a/web/readme.md b/web/readme.md index 942ff80ba..b0c6f207e 100644 --- a/web/readme.md +++ b/web/readme.md @@ -22,10 +22,8 @@ You should see a message printing the URL to access: `http://localhost:8080` ## Deploying to GitHub Pages -This project uses [peanut][peanut] to build the samples and commit the output -to the gh-pages branch. To deploy, run these commands in the `web/` directory: - -Install the peanut command: +This project uses a GitHub action to deploy update the `gh-pages` branch. To +do this manually, you can also use `package:peanut`: ```console $ flutter pub global activate peanut diff --git a/web/samples_index/lib/src/samples.yaml b/web/samples_index/lib/src/samples.yaml index 2d29a1366..f2daeea2c 100644 --- a/web/samples_index/lib/src/samples.yaml +++ b/web/samples_index/lib/src/samples.yaml @@ -508,22 +508,6 @@ samples: web: web/slide_puzzle type: demo - - name: Timeflow - author: Fabian Stein - screenshots: - - url: images/timeflow1.png - alt: Timeflow screenshot - source: https://github.com/Fabian-Stein/timeflow - description: > - A gentle animation that provides a calming experience to stressed developers. - difficulty: advanced - widgets: [] - packages: [] - platforms: ['web'] - tags: ['demo', 'animation'] - web: web/timeflow - type: demo - - name: Dice author: Jaime Blasco screenshots: diff --git a/web/samples_index/tool/grind.dart b/web/samples_index/tool/grind.dart index 5b385763f..450c62538 100644 --- a/web/samples_index/tool/grind.dart +++ b/web/samples_index/tool/grind.dart @@ -19,7 +19,7 @@ void testCli() async => await TestRunner().testAsync(platformSelector: 'vm'); @Task() void analyze() { - PubApp.local('tuneup')..run(['check']); + PubApp.local('tuneup').run(['check']); } @Task('deploy') diff --git a/web/slide_puzzle b/web/slide_puzzle index 5ef5526ac..5c590d0b0 160000 --- a/web/slide_puzzle +++ b/web/slide_puzzle @@ -1 +1 @@ -Subproject commit 5ef5526acb58f9ffb6f3fb22118cbd825613dc73 +Subproject commit 5c590d0b0252cf3d7cbddf9998d7807b87c91550 diff --git a/web/timeflow/LICENSE b/web/timeflow/LICENSE deleted file mode 100644 index b6d835594..000000000 --- a/web/timeflow/LICENSE +++ /dev/null @@ -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. \ No newline at end of file diff --git a/web/timeflow/README.md b/web/timeflow/README.md deleted file mode 100644 index 02c047fcb..000000000 --- a/web/timeflow/README.md +++ /dev/null @@ -1,103 +0,0 @@ -A gentle animation that provides a calming experience to stressed developers. - -Contributed as part of the Flutter Create 5K challenge by Fabian Stein. -Original source at -[github.com/Fabian-Stein/timeflow](https://github.com/Fabian-Stein/timeflow). - -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 - diff --git a/web/timeflow/assets/preview.png b/web/timeflow/assets/preview.png deleted file mode 100644 index 62f052388..000000000 Binary files a/web/timeflow/assets/preview.png and /dev/null differ diff --git a/web/timeflow/lib/infinite_listview.dart b/web/timeflow/lib/infinite_listview.dart deleted file mode 100644 index 33e52ef8f..000000000 --- a/web/timeflow/lib/infinite_listview.dart +++ /dev/null @@ -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 slivers = _buildSlivers(context, negative: false); - final List 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: [ - 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 _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 [sliver]; - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties.add(new EnumProperty('scrollDirection', scrollDirection)); - properties.add(new FlagProperty('reverse', - value: reverse, ifTrue: 'reversed', showName: true)); - properties.add(new DiagnosticsProperty( - 'controller', controller, - showName: false, defaultValue: null)); - properties.add(new DiagnosticsProperty('physics', physics, - showName: false, defaultValue: null)); - properties.add(new DiagnosticsProperty( - '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; -} diff --git a/web/timeflow/lib/main.dart b/web/timeflow/lib/main.dart deleted file mode 100644 index eb257f274..000000000 --- a/web/timeflow/lib/main.dart +++ /dev/null @@ -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 createState() => TM(); -} - -enum SI { pause, play, stop } -List triangles; -var percent = 0.0, cTime = 0.0, dur = 120000.0, rng = Random(), rebuild = true; - -class TM extends State { - 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( - 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 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 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 cP() { - List 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 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); - } - } -} diff --git a/web/timeflow/lib/numberpicker.dart b/web/timeflow/lib/numberpicker.dart deleted file mode 100644 index 121c65750..000000000 --- a/web/timeflow/lib/numberpicker.dart +++ /dev/null @@ -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 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: [ - _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 createState() => - new _NumberPickerDialogControllerState( - initialIntegerValue, initialDoubleValue); -} - -class _NumberPickerDialogControllerState extends State { - 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), - ], - ); - } -} diff --git a/web/timeflow/pubspec.lock b/web/timeflow/pubspec.lock deleted file mode 100644 index 49eea0373..000000000 --- a/web/timeflow/pubspec.lock +++ /dev/null @@ -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" diff --git a/web/timeflow/pubspec.yaml b/web/timeflow/pubspec.yaml deleted file mode 100644 index dd3aa6641..000000000 --- a/web/timeflow/pubspec.yaml +++ /dev/null @@ -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 \ No newline at end of file diff --git a/web/timeflow/web/index.html b/web/timeflow/web/index.html deleted file mode 100644 index 1785ebb8b..000000000 --- a/web/timeflow/web/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - -