+bezier
This commit is contained in:
42
.idea/libraries/Dart_SDK.xml
generated
42
.idea/libraries/Dart_SDK.xml
generated
@@ -1,27 +1,27 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="Dart SDK">
|
<library name="Dart SDK">
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="file://$PROJECT_DIR$/../../flutter/flutter/bin/cache/dart-sdk/lib/async" />
|
<root url="file://$USER_HOME$/git/flutter/bin/cache/dart-sdk/lib/async" />
|
||||||
<root url="file://$PROJECT_DIR$/../../flutter/flutter/bin/cache/dart-sdk/lib/cli" />
|
<root url="file://$USER_HOME$/git/flutter/bin/cache/dart-sdk/lib/cli" />
|
||||||
<root url="file://$PROJECT_DIR$/../../flutter/flutter/bin/cache/dart-sdk/lib/collection" />
|
<root url="file://$USER_HOME$/git/flutter/bin/cache/dart-sdk/lib/collection" />
|
||||||
<root url="file://$PROJECT_DIR$/../../flutter/flutter/bin/cache/dart-sdk/lib/convert" />
|
<root url="file://$USER_HOME$/git/flutter/bin/cache/dart-sdk/lib/convert" />
|
||||||
<root url="file://$PROJECT_DIR$/../../flutter/flutter/bin/cache/dart-sdk/lib/core" />
|
<root url="file://$USER_HOME$/git/flutter/bin/cache/dart-sdk/lib/core" />
|
||||||
<root url="file://$PROJECT_DIR$/../../flutter/flutter/bin/cache/dart-sdk/lib/developer" />
|
<root url="file://$USER_HOME$/git/flutter/bin/cache/dart-sdk/lib/developer" />
|
||||||
<root url="file://$PROJECT_DIR$/../../flutter/flutter/bin/cache/dart-sdk/lib/ffi" />
|
<root url="file://$USER_HOME$/git/flutter/bin/cache/dart-sdk/lib/ffi" />
|
||||||
<root url="file://$PROJECT_DIR$/../../flutter/flutter/bin/cache/dart-sdk/lib/html" />
|
<root url="file://$USER_HOME$/git/flutter/bin/cache/dart-sdk/lib/html" />
|
||||||
<root url="file://$PROJECT_DIR$/../../flutter/flutter/bin/cache/dart-sdk/lib/indexed_db" />
|
<root url="file://$USER_HOME$/git/flutter/bin/cache/dart-sdk/lib/indexed_db" />
|
||||||
<root url="file://$PROJECT_DIR$/../../flutter/flutter/bin/cache/dart-sdk/lib/io" />
|
<root url="file://$USER_HOME$/git/flutter/bin/cache/dart-sdk/lib/io" />
|
||||||
<root url="file://$PROJECT_DIR$/../../flutter/flutter/bin/cache/dart-sdk/lib/isolate" />
|
<root url="file://$USER_HOME$/git/flutter/bin/cache/dart-sdk/lib/isolate" />
|
||||||
<root url="file://$PROJECT_DIR$/../../flutter/flutter/bin/cache/dart-sdk/lib/js" />
|
<root url="file://$USER_HOME$/git/flutter/bin/cache/dart-sdk/lib/js" />
|
||||||
<root url="file://$PROJECT_DIR$/../../flutter/flutter/bin/cache/dart-sdk/lib/js_util" />
|
<root url="file://$USER_HOME$/git/flutter/bin/cache/dart-sdk/lib/js_util" />
|
||||||
<root url="file://$PROJECT_DIR$/../../flutter/flutter/bin/cache/dart-sdk/lib/math" />
|
<root url="file://$USER_HOME$/git/flutter/bin/cache/dart-sdk/lib/math" />
|
||||||
<root url="file://$PROJECT_DIR$/../../flutter/flutter/bin/cache/dart-sdk/lib/mirrors" />
|
<root url="file://$USER_HOME$/git/flutter/bin/cache/dart-sdk/lib/mirrors" />
|
||||||
<root url="file://$PROJECT_DIR$/../../flutter/flutter/bin/cache/dart-sdk/lib/svg" />
|
<root url="file://$USER_HOME$/git/flutter/bin/cache/dart-sdk/lib/svg" />
|
||||||
<root url="file://$PROJECT_DIR$/../../flutter/flutter/bin/cache/dart-sdk/lib/typed_data" />
|
<root url="file://$USER_HOME$/git/flutter/bin/cache/dart-sdk/lib/typed_data" />
|
||||||
<root url="file://$PROJECT_DIR$/../../flutter/flutter/bin/cache/dart-sdk/lib/wasm" />
|
<root url="file://$USER_HOME$/git/flutter/bin/cache/dart-sdk/lib/wasm" />
|
||||||
<root url="file://$PROJECT_DIR$/../../flutter/flutter/bin/cache/dart-sdk/lib/web_audio" />
|
<root url="file://$USER_HOME$/git/flutter/bin/cache/dart-sdk/lib/web_audio" />
|
||||||
<root url="file://$PROJECT_DIR$/../../flutter/flutter/bin/cache/dart-sdk/lib/web_gl" />
|
<root url="file://$USER_HOME$/git/flutter/bin/cache/dart-sdk/lib/web_gl" />
|
||||||
<root url="file://$PROJECT_DIR$/../../flutter/flutter/bin/cache/dart-sdk/lib/web_sql" />
|
<root url="file://$USER_HOME$/git/flutter/bin/cache/dart-sdk/lib/web_sql" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
|||||||
16
.idea/workspace.xml
generated
16
.idea/workspace.xml
generated
@@ -2,18 +2,18 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="e30161f9-0e11-4901-861c-a8265e376258" name="Default Changelist" comment="">
|
<list default="true" id="e30161f9-0e11-4901-861c-a8265e376258" name="Default Changelist" comment="">
|
||||||
<change afterPath="$PROJECT_DIR$/lib/src/ux_app.dart" afterDir="false" />
|
<change afterPath="$PROJECT_DIR$/lib/src/bezier.dart" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/libraries/Dart_SDK.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/libraries/Dart_SDK.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/libraries/Dart_SDK.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/libraries/Dart_SDK.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/example/android/gradle.properties" beforeDir="false" afterPath="$PROJECT_DIR$/example/android/gradle.properties" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/example/.flutter-plugins-dependencies" beforeDir="false" afterPath="$PROJECT_DIR$/example/.flutter-plugins-dependencies" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/example/ios/Flutter/flutter_export_environment.sh" beforeDir="false" afterPath="$PROJECT_DIR$/example/ios/Flutter/flutter_export_environment.sh" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/example/ios/Flutter/flutter_export_environment.sh" beforeDir="false" afterPath="$PROJECT_DIR$/example/ios/Flutter/flutter_export_environment.sh" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/example/lib/main.dart" beforeDir="false" afterPath="$PROJECT_DIR$/example/lib/main.dart" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/example/lib/main.dart" beforeDir="false" afterPath="$PROJECT_DIR$/example/lib/main.dart" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/example/pubspec.lock" beforeDir="false" afterPath="$PROJECT_DIR$/example/pubspec.lock" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/example/pubspec.lock" beforeDir="false" afterPath="$PROJECT_DIR$/example/pubspec.lock" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/lib/src/json_extension.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/json_extension.dart" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/lib/src/note.dart" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/lib/src/ux_app.dart" beforeDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/lib/ux.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/ux.dart" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/lib/ux.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/ux.dart" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/pubspec.lock" beforeDir="false" afterPath="$PROJECT_DIR$/pubspec.lock" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/pubspec.lock" beforeDir="false" afterPath="$PROJECT_DIR$/pubspec.lock" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/pubspec.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/pubspec.yaml" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/test/ux_test.dart" beforeDir="false" afterPath="$PROJECT_DIR$/test/ux_test.dart" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/ux.iml" beforeDir="false" afterPath="$PROJECT_DIR$/ux.iml" afterDir="false" />
|
|
||||||
</list>
|
</list>
|
||||||
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
|
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ExecutionTargetManager" SELECTED_TARGET="Pixel_2_API_R" />
|
<component name="ExecutionTargetManager" SELECTED_TARGET="Nexus_5X_API_29_x86" />
|
||||||
<component name="FileTemplateManagerImpl">
|
<component name="FileTemplateManagerImpl">
|
||||||
<option name="RECENT_TEMPLATES">
|
<option name="RECENT_TEMPLATES">
|
||||||
<list>
|
<list>
|
||||||
@@ -77,7 +77,9 @@
|
|||||||
<map>
|
<map>
|
||||||
<entry key="MAIN">
|
<entry key="MAIN">
|
||||||
<value>
|
<value>
|
||||||
<State />
|
<State>
|
||||||
|
<option name="COLUMN_ORDER" />
|
||||||
|
</State>
|
||||||
</value>
|
</value>
|
||||||
</entry>
|
</entry>
|
||||||
</map>
|
</map>
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"_info":"// This is a generated file; do not edit or check into version control.","dependencyGraph":[{"name":"ux","dependencies":[]}]}
|
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"ux","path":"/Users/zen/swipelab/ux/","dependencies":[]}],"android":[{"name":"ux","path":"/Users/zen/swipelab/ux/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"ux","dependencies":[]}],"date_created":"2020-05-25 08:49:54.524717","version":"1.17.1"}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# This is a generated file; do not edit or check into version control.
|
# This is a generated file; do not edit or check into version control.
|
||||||
export "FLUTTER_ROOT=/home/agra/git/flutter/flutter"
|
export "FLUTTER_ROOT=/Users/zen/git/flutter"
|
||||||
export "FLUTTER_APPLICATION_PATH=/home/agra/git/swipelab/ux/example"
|
export "FLUTTER_APPLICATION_PATH=/Users/zen/swipelab/ux/example"
|
||||||
export "FLUTTER_TARGET=lib/main.dart"
|
export "FLUTTER_TARGET=lib/main.dart"
|
||||||
export "FLUTTER_BUILD_DIR=build"
|
export "FLUTTER_BUILD_DIR=build"
|
||||||
export "SYMROOT=${SOURCE_ROOT}/../build/ios"
|
export "SYMROOT=${SOURCE_ROOT}/../build/ios"
|
||||||
export "FLUTTER_FRAMEWORK_DIR=/home/agra/git/flutter/flutter/bin/cache/artifacts/engine/ios"
|
export "OTHER_LDFLAGS=$(inherited) -framework Flutter"
|
||||||
|
export "FLUTTER_FRAMEWORK_DIR=/Users/zen/git/flutter/bin/cache/artifacts/engine/ios"
|
||||||
export "FLUTTER_BUILD_NAME=1.0.0"
|
export "FLUTTER_BUILD_NAME=1.0.0"
|
||||||
export "FLUTTER_BUILD_NUMBER=1"
|
export "FLUTTER_BUILD_NUMBER=1"
|
||||||
|
|||||||
@@ -30,9 +30,6 @@ class _MyAppState extends State<MyApp> {
|
|||||||
platformVersion = 'Failed to get platform version.';
|
platformVersion = 'Failed to get platform version.';
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the widget was removed from the tree while the asynchronous platform
|
|
||||||
// message was in flight, we want to discard the reply rather than calling
|
|
||||||
// setState to update our non-existent appearance.
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -42,33 +39,30 @@ class _MyAppState extends State<MyApp> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return UxApp(
|
return MaterialApp(
|
||||||
child: MaterialApp(
|
routes: {
|
||||||
routes: {
|
'/': (context) => Scaffold(
|
||||||
'/': (context) => Scaffold(
|
appBar: AppBar(
|
||||||
appBar: AppBar(
|
title: const Text('Plugin example app'),
|
||||||
title: const Text('Plugin example app'),
|
),
|
||||||
|
body: Builder(
|
||||||
|
builder: (context) => ListView(
|
||||||
|
padding: EdgeInsets.only(top: 48),
|
||||||
|
children: [
|
||||||
|
ListTile(title: Text('Running on: $_platformVersion\n')),
|
||||||
|
ListTile(
|
||||||
|
title: Text('Show a simple note'),
|
||||||
|
//onTap: () => context.showText('This is a simple note'),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text('Show modal note'),
|
||||||
|
//onTap: () => context.showText('This is a modal note', backdropBlur: 6, modal: true),
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
body: Builder(
|
),
|
||||||
builder: (context) => ListView(
|
)
|
||||||
padding: EdgeInsets.only(top: 48),
|
},
|
||||||
children: [
|
|
||||||
ListTile(title: Text('Running on: $_platformVersion\n')),
|
|
||||||
ListTile(
|
|
||||||
title: Text('Show a simple note'),
|
|
||||||
onTap: () => context.showText('This is a simple note'),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text('Show modal note'),
|
|
||||||
onTap: () => context.showText('This is a modal note',
|
|
||||||
backdropBlur: 6, modal: true),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,42 +7,42 @@ packages:
|
|||||||
name: archive
|
name: archive
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.11"
|
version: "2.0.13"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: args
|
name: args
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.2"
|
version: "1.6.0"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: async
|
name: async
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.0"
|
version: "2.4.1"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: boolean_selector
|
name: boolean_selector
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "2.0.0"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: charcode
|
name: charcode
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.2"
|
version: "1.1.3"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.14.11"
|
version: "1.14.12"
|
||||||
convert:
|
convert:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -56,7 +56,7 @@ packages:
|
|||||||
name: crypto
|
name: crypto
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.4"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -80,7 +80,7 @@ packages:
|
|||||||
name: image
|
name: image
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.1.12"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -102,13 +102,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.6.4"
|
version: "1.6.4"
|
||||||
pedantic:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: pedantic
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.8.0+1"
|
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -122,7 +115,7 @@ packages:
|
|||||||
name: quiver
|
name: quiver
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.5"
|
version: "2.1.3"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -134,7 +127,7 @@ packages:
|
|||||||
name: source_span
|
name: source_span
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.5"
|
version: "1.7.0"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -169,7 +162,7 @@ packages:
|
|||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.11"
|
version: "0.2.15"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -197,6 +190,6 @@ packages:
|
|||||||
name: xml
|
name: xml
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.5.0"
|
version: "3.6.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.6.0 <3.0.0"
|
dart: ">=2.6.0 <3.0.0"
|
||||||
|
|||||||
143
lib/src/bezier.dart
Normal file
143
lib/src/bezier.dart
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
double bezierLength(Bezier bezier, [double steps = 10]) {
|
||||||
|
assert(bezier != null && steps != 0);
|
||||||
|
final step = 1 / steps;
|
||||||
|
var c = bezier.point(0);
|
||||||
|
var length = 0.0;
|
||||||
|
for (var t = step; t <= 1; t += step) {
|
||||||
|
final p = bezier.point(t);
|
||||||
|
length += (p - c).distance;
|
||||||
|
c = p;
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Bezier {
|
||||||
|
Offset point(double t);
|
||||||
|
|
||||||
|
double get length => bezierLength(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
class LinearBezier extends Bezier {
|
||||||
|
Offset p0, p1;
|
||||||
|
|
||||||
|
LinearBezier(this.p0, this.p1);
|
||||||
|
|
||||||
|
Offset point(double t) {
|
||||||
|
return p0 + (p1 - p0) * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
double get length => (p1 - p0).distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
class QuadraticBezier extends Bezier {
|
||||||
|
double _quadraticBezier(double t, double p0, double p1, double p2) {
|
||||||
|
//final lt = 1 - t;
|
||||||
|
//return lt * (lt * p0 + t * p1) + t * (lt * p1 + t * p2);
|
||||||
|
//return pow(1 - t, 2) * p0 + 2 * (1 - t) * t * p1 + pow(t, 2) * p2;
|
||||||
|
return p1 + pow(1 - t, 2) * (p0 - p1) + pow(t, 2) * (p2 - p1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Offset p0, p1, p2;
|
||||||
|
|
||||||
|
QuadraticBezier(this.p0, this.p1, this.p2);
|
||||||
|
|
||||||
|
Offset point(double t) {
|
||||||
|
return Offset(
|
||||||
|
_quadraticBezier(t, p0.dx, p1.dx, p2.dx),
|
||||||
|
_quadraticBezier(t, p0.dy, p1.dy, p2.dy),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CubicBezier extends Bezier {
|
||||||
|
double _cubicBezier(double t, double p0, double p1, double p2, double p3) {
|
||||||
|
return pow(1 - t, 3) * p0 +
|
||||||
|
3 * t * pow(1 - t, 2) * p1 +
|
||||||
|
3 * pow(t, 2) * (1 - t) * p2 +
|
||||||
|
pow(t, 3) * p3;
|
||||||
|
}
|
||||||
|
|
||||||
|
Offset p0, p1, p2, p3;
|
||||||
|
|
||||||
|
CubicBezier(this.p0, this.p1, this.p2, this.p3);
|
||||||
|
|
||||||
|
Offset point(double t) => Offset(
|
||||||
|
_cubicBezier(t, p0.dx, p1.dx, p2.dx, p3.dx),
|
||||||
|
_cubicBezier(t, p0.dy, p1.dy, p2.dy, p3.dy),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class PathBezier extends Bezier {
|
||||||
|
double _length = 0;
|
||||||
|
|
||||||
|
double get length => _length;
|
||||||
|
|
||||||
|
List<Bezier> _curves = [];
|
||||||
|
List<double> _lens = [];
|
||||||
|
|
||||||
|
final Offset p0;
|
||||||
|
Offset _p0;
|
||||||
|
|
||||||
|
PathBezier(this.p0) : _p0 = p0;
|
||||||
|
|
||||||
|
static PathBezier roundedRect(RRect rrect) {
|
||||||
|
return PathBezier(Offset(rrect.left + rrect.tlRadiusX, rrect.top))
|
||||||
|
..lineTo(Offset(rrect.right - rrect.trRadiusX, rrect.top))
|
||||||
|
..quadTo(
|
||||||
|
Offset(rrect.right, rrect.top), Offset(rrect.right, rrect.trRadiusY))
|
||||||
|
..lineTo(Offset(rrect.right, rrect.bottom - rrect.brRadiusY))
|
||||||
|
..quadTo(Offset(rrect.right, rrect.bottom),
|
||||||
|
Offset(rrect.right - rrect.brRadiusX, rrect.bottom))
|
||||||
|
..lineTo(Offset(rrect.left + rrect.brRadiusX, rrect.bottom))
|
||||||
|
..quadTo(Offset(rrect.left, rrect.bottom),
|
||||||
|
Offset(rrect.left, rrect.bottom - rrect.blRadiusY))
|
||||||
|
..lineTo(Offset(rrect.left, rrect.top + rrect.tlRadiusX))
|
||||||
|
..quadTo(Offset(rrect.left, rrect.top),
|
||||||
|
Offset(rrect.left + rrect.tlRadiusX, rrect.top));
|
||||||
|
}
|
||||||
|
|
||||||
|
_add(Bezier bezier, Offset pn) {
|
||||||
|
_curves.add(bezier);
|
||||||
|
final bl = bezierLength(bezier);
|
||||||
|
_lens.add(bl);
|
||||||
|
_length += bl;
|
||||||
|
_p0 = pn;
|
||||||
|
}
|
||||||
|
|
||||||
|
lineTo(Offset p1) => _add(LinearBezier(_p0, p1), p1);
|
||||||
|
|
||||||
|
quadTo(Offset p1, Offset p2) => _add(QuadraticBezier(_p0, p1, p2), p2);
|
||||||
|
|
||||||
|
cubeTo(Offset p1, Offset p2, Offset p3) =>
|
||||||
|
_add(CubicBezier(_p0, p1, p2, p3), p3);
|
||||||
|
|
||||||
|
relativeLineTo(Offset p1) => lineTo(
|
||||||
|
p1 + _p0,
|
||||||
|
);
|
||||||
|
|
||||||
|
relativeQuadTo(Offset p1, Offset p2) => quadTo(
|
||||||
|
p1 + p0,
|
||||||
|
p1 + p2 + p0,
|
||||||
|
);
|
||||||
|
|
||||||
|
relativeCubeTo(Offset p1, Offset p2, Offset p3) => cubeTo(
|
||||||
|
p0 + p1,
|
||||||
|
p0 + p1 + p2,
|
||||||
|
p0 + p1 + p2 + p3,
|
||||||
|
);
|
||||||
|
|
||||||
|
Offset point(double t) {
|
||||||
|
if (_length == 0) return p0;
|
||||||
|
var distance = _length * t;
|
||||||
|
var index = 0;
|
||||||
|
while (index < _lens.length && distance > _lens[index]) {
|
||||||
|
distance -= _lens[index];
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
if (index == _lens.length) return _p0;
|
||||||
|
return _curves[index].point(distance / _lens[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
typedef T FromJson<T>(Map<String, dynamic> json);
|
typedef T _JsonConvert<T>(Map<String, dynamic> json);
|
||||||
|
|
||||||
class Json {
|
class Json {
|
||||||
static List<T> list<T>(List json, FromJson<T> fromJson) => json == null
|
static List<T> list<T>(List json, _JsonConvert<T> fromJson) => json == null
|
||||||
? []
|
? []
|
||||||
: json.cast<Map<String, dynamic>>().map(fromJson).toList();
|
: json.cast<Map<String, dynamic>>().map(fromJson).toList();
|
||||||
|
|
||||||
static Map<String, T> map<T>(Map json, FromJson<T> fromJson) => json == null
|
static Map<String, T> map<T>(Map json, _JsonConvert<T> fromJson) => json == null
|
||||||
? {}
|
? {}
|
||||||
: Map.fromEntries(
|
: Map.fromEntries(
|
||||||
json.entries.map((e) => MapEntry(e.key, fromJson(e.value))));
|
json.entries.map((e) => MapEntry(e.key, fromJson(e.value))));
|
||||||
|
|||||||
@@ -1,555 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/scheduler.dart';
|
|
||||||
import 'package:ux/src/util.dart';
|
|
||||||
|
|
||||||
const String NOTE_ROUTE = "/ux/note";
|
|
||||||
|
|
||||||
typedef void NoteStatusCallback(NoteStatus status);
|
|
||||||
typedef void OnTap(Note note);
|
|
||||||
|
|
||||||
class Note<T extends Object> extends StatefulWidget {
|
|
||||||
Note(
|
|
||||||
{Key key,
|
|
||||||
this.child,
|
|
||||||
this.onTap,
|
|
||||||
this.duration = const Duration(seconds: 3),
|
|
||||||
this.isDismissible = true,
|
|
||||||
this.dismissDirection = NoteDismissDirection.Vertical,
|
|
||||||
this.position = NotePosition.Bottom,
|
|
||||||
this.forwardAnimationCurve = Curves.easeOutCirc,
|
|
||||||
this.reverseAnimationCurve = Curves.easeOutCirc,
|
|
||||||
this.animationDuration = const Duration(seconds: 1),
|
|
||||||
this.onStatusChanged,
|
|
||||||
this.isModal = false,
|
|
||||||
this.modalBackdropBlur,
|
|
||||||
this.modalBackgroundColor})
|
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
final NoteStatusCallback onStatusChanged;
|
|
||||||
|
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
final OnTap onTap;
|
|
||||||
|
|
||||||
final Duration duration;
|
|
||||||
|
|
||||||
final bool isDismissible;
|
|
||||||
|
|
||||||
final NotePosition position;
|
|
||||||
|
|
||||||
final NoteDismissDirection dismissDirection;
|
|
||||||
|
|
||||||
final Curve forwardAnimationCurve;
|
|
||||||
|
|
||||||
/// The [Curve] animation used when dismiss() is called. [Curves.fastOutSlowIn] is default
|
|
||||||
final Curve reverseAnimationCurve;
|
|
||||||
|
|
||||||
/// Use it to speed up or slow down the animation duration
|
|
||||||
final Duration animationDuration;
|
|
||||||
|
|
||||||
final bool isModal;
|
|
||||||
final double modalBackdropBlur;
|
|
||||||
final Color modalBackgroundColor;
|
|
||||||
|
|
||||||
NoteRoute<T> _noteRoute;
|
|
||||||
|
|
||||||
Future<T> show(BuildContext context) async {
|
|
||||||
_noteRoute = NoteRoute<T>(note: this);
|
|
||||||
return await Navigator.of(context, rootNavigator: false).push(_noteRoute);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<T> dismiss([T result]) async {
|
|
||||||
// If route was never initialized, do nothing
|
|
||||||
if (_noteRoute == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_noteRoute.isCurrent) {
|
|
||||||
_noteRoute.navigator.pop(result);
|
|
||||||
return _noteRoute.completed;
|
|
||||||
} else if (_noteRoute.isActive) {
|
|
||||||
_noteRoute.navigator.removeRoute(_noteRoute);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isShowing() {
|
|
||||||
return _noteRoute?.currentStatus == NoteStatus.Visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isDismissed() {
|
|
||||||
return _noteRoute?.currentStatus == NoteStatus.Dismissed;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
State createState() {
|
|
||||||
return _NoteState<T>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _NoteState<K extends Object> extends State<Note>
|
|
||||||
with TickerProviderStateMixin {
|
|
||||||
GlobalKey _backgroundBoxKey;
|
|
||||||
NoteStatus currentStatus;
|
|
||||||
AnimationController _fadeController;
|
|
||||||
FocusScopeNode _focusNode;
|
|
||||||
FocusAttachment _focusAttachment;
|
|
||||||
Completer<Size> _boxHeightCompleter;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
|
|
||||||
_backgroundBoxKey = GlobalKey();
|
|
||||||
_boxHeightCompleter = Completer<Size>();
|
|
||||||
|
|
||||||
_configureLeftBarFuture();
|
|
||||||
|
|
||||||
_focusNode = FocusScopeNode();
|
|
||||||
_focusAttachment = _focusNode.attach(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_fadeController?.dispose();
|
|
||||||
|
|
||||||
_focusAttachment.detach();
|
|
||||||
_focusNode.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _configureLeftBarFuture() {
|
|
||||||
SchedulerBinding.instance.addPostFrameCallback(
|
|
||||||
(_) {
|
|
||||||
final keyContext = _backgroundBoxKey.currentContext;
|
|
||||||
|
|
||||||
if (keyContext != null) {
|
|
||||||
final RenderBox box = keyContext.findRenderObject();
|
|
||||||
_boxHeightCompleter.complete(box.size);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Material(
|
|
||||||
color: Colors.transparent,
|
|
||||||
child: SafeArea(
|
|
||||||
minimum: widget.position == NotePosition.Bottom
|
|
||||||
? EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom)
|
|
||||||
: EdgeInsets.only(top: MediaQuery.of(context).viewInsets.top),
|
|
||||||
bottom: widget.position == NotePosition.Bottom,
|
|
||||||
top: widget.position == NotePosition.Top,
|
|
||||||
left: false,
|
|
||||||
right: false,
|
|
||||||
child: widget.child,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum NotePosition { Top, Bottom }
|
|
||||||
|
|
||||||
enum NoteDismissDirection { Horizontal, Vertical }
|
|
||||||
|
|
||||||
enum NoteStatus { Visible, Dismissed, FadeIn, FadeOut }
|
|
||||||
|
|
||||||
class NoteRoute<T> extends OverlayRoute<T> {
|
|
||||||
final Note note;
|
|
||||||
final Builder _builder;
|
|
||||||
final Completer<T> _transitionCompleter = Completer<T>();
|
|
||||||
final NoteStatusCallback _onStatusChanged;
|
|
||||||
|
|
||||||
Animation<double> _filterBlurAnimation;
|
|
||||||
Animation<Color> _filterColorAnimation;
|
|
||||||
Alignment _initialAlignment;
|
|
||||||
Alignment _endAlignment;
|
|
||||||
bool _wasDismissedBySwipe = false;
|
|
||||||
Timer _timer;
|
|
||||||
T _result;
|
|
||||||
NoteStatus currentStatus;
|
|
||||||
|
|
||||||
NoteRoute({
|
|
||||||
@required this.note,
|
|
||||||
RouteSettings settings = const RouteSettings(name: NOTE_ROUTE),
|
|
||||||
}) : _builder = Builder(builder: (BuildContext innerContext) {
|
|
||||||
return GestureDetector(
|
|
||||||
child: note,
|
|
||||||
onTap: note.onTap != null ? () => note.onTap(note) : null,
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
_onStatusChanged = note.onStatusChanged,
|
|
||||||
super(settings: settings) {
|
|
||||||
_configureAlignment(this.note.position);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _configureAlignment(NotePosition position) {
|
|
||||||
switch (note.position) {
|
|
||||||
case NotePosition.Top:
|
|
||||||
{
|
|
||||||
_initialAlignment = Alignment(-1.0, -2.0);
|
|
||||||
_endAlignment = Alignment(-1.0, -1.0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case NotePosition.Bottom:
|
|
||||||
{
|
|
||||||
_initialAlignment = Alignment(-1.0, 2.0);
|
|
||||||
_endAlignment = Alignment(-1.0, 1.0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<T> get completed => _transitionCompleter.future;
|
|
||||||
|
|
||||||
bool get opaque => false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Iterable<OverlayEntry> createOverlayEntries() {
|
|
||||||
final List<OverlayEntry> overlays = [];
|
|
||||||
|
|
||||||
if (note.isModal) {
|
|
||||||
overlays.add(
|
|
||||||
OverlayEntry(
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: note.isDismissible ? () => note.dismiss() : null,
|
|
||||||
child: _createBackgroundOverlay(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
maintainState: false,
|
|
||||||
opaque: opaque),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
overlays.add(
|
|
||||||
OverlayEntry(
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
final Widget annotatedChild = Semantics(
|
|
||||||
child: AlignTransition(
|
|
||||||
alignment: _animation,
|
|
||||||
child: note.isDismissible
|
|
||||||
? _getDismissibleNote(_builder)
|
|
||||||
: _builder,
|
|
||||||
),
|
|
||||||
focused: false,
|
|
||||||
container: true,
|
|
||||||
explicitChildNodes: true,
|
|
||||||
);
|
|
||||||
return annotatedChild;
|
|
||||||
},
|
|
||||||
maintainState: false,
|
|
||||||
opaque: opaque),
|
|
||||||
);
|
|
||||||
|
|
||||||
return overlays;
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _createBackgroundOverlay() {
|
|
||||||
if (_filterBlurAnimation != null && _filterColorAnimation != null) {
|
|
||||||
return AnimatedBuilder(
|
|
||||||
animation: _filterBlurAnimation,
|
|
||||||
builder: (context, child) {
|
|
||||||
return BackdropFilter(
|
|
||||||
filter: ImageFilter.blur(
|
|
||||||
sigmaX: _filterBlurAnimation.value,
|
|
||||||
sigmaY: _filterBlurAnimation.value),
|
|
||||||
child: Container(
|
|
||||||
constraints: BoxConstraints.expand(),
|
|
||||||
color: _filterColorAnimation.value,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_filterBlurAnimation != null) {
|
|
||||||
return AnimatedBuilder(
|
|
||||||
animation: _filterBlurAnimation,
|
|
||||||
builder: (context, child) {
|
|
||||||
return BackdropFilter(
|
|
||||||
filter: ImageFilter.blur(
|
|
||||||
sigmaX: _filterBlurAnimation.value,
|
|
||||||
sigmaY: _filterBlurAnimation.value),
|
|
||||||
child: Container(
|
|
||||||
constraints: BoxConstraints.expand(),
|
|
||||||
color: Colors.transparent,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_filterColorAnimation != null) {
|
|
||||||
AnimatedBuilder(
|
|
||||||
animation: _filterColorAnimation,
|
|
||||||
builder: (context, child) {
|
|
||||||
return Container(
|
|
||||||
constraints: BoxConstraints.expand(),
|
|
||||||
color: _filterColorAnimation.value,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
constraints: BoxConstraints.expand(),
|
|
||||||
color: Colors.transparent,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String dismissKey = nextId().toString();
|
|
||||||
|
|
||||||
Widget _getDismissibleNote(Widget child) {
|
|
||||||
return Dismissible(
|
|
||||||
direction: _getDismissDirection(),
|
|
||||||
resizeDuration: null,
|
|
||||||
confirmDismiss: (_) {
|
|
||||||
if (currentStatus == NoteStatus.FadeIn ||
|
|
||||||
currentStatus == NoteStatus.FadeOut) {
|
|
||||||
return Future.value(false);
|
|
||||||
}
|
|
||||||
return Future.value(true);
|
|
||||||
},
|
|
||||||
key: Key(dismissKey),
|
|
||||||
onDismissed: (_) {
|
|
||||||
_cancelTimer();
|
|
||||||
_wasDismissedBySwipe = true;
|
|
||||||
|
|
||||||
if (isCurrent) {
|
|
||||||
navigator.pop();
|
|
||||||
} else {
|
|
||||||
navigator.removeRoute(this);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: _builder,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
DismissDirection _getDismissDirection() {
|
|
||||||
if (note.dismissDirection == NoteDismissDirection.Horizontal) {
|
|
||||||
return DismissDirection.horizontal;
|
|
||||||
} else {
|
|
||||||
if (note.position == NotePosition.Top) {
|
|
||||||
return DismissDirection.up;
|
|
||||||
} else {
|
|
||||||
return DismissDirection.down;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool get finishedWhenPopped =>
|
|
||||||
_controller.status == AnimationStatus.dismissed;
|
|
||||||
|
|
||||||
/// The animation that drives the route's transition and the previous route's
|
|
||||||
/// forward transition.
|
|
||||||
Animation<Alignment> get animation => _animation;
|
|
||||||
Animation<Alignment> _animation;
|
|
||||||
|
|
||||||
/// The animation controller that the route uses to drive the transitions.
|
|
||||||
///
|
|
||||||
/// The animation itself is exposed by the [animation] property.
|
|
||||||
@protected
|
|
||||||
AnimationController get controller => _controller;
|
|
||||||
AnimationController _controller;
|
|
||||||
|
|
||||||
/// Called to create the animation controller that will drive the transitions to
|
|
||||||
/// this route from the previous one, and back to the previous route from this
|
|
||||||
/// one.
|
|
||||||
AnimationController createAnimationController() {
|
|
||||||
assert(!_transitionCompleter.isCompleted,
|
|
||||||
'Cannot reuse a $runtimeType after disposing it.');
|
|
||||||
assert(note.animationDuration != null &&
|
|
||||||
note.animationDuration >= Duration.zero);
|
|
||||||
return AnimationController(
|
|
||||||
duration: note.animationDuration,
|
|
||||||
debugLabel: debugLabel,
|
|
||||||
vsync: navigator,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called to create the animation that exposes the current progress of
|
|
||||||
/// the transition controlled by the animation controller created by
|
|
||||||
/// [createAnimationController()].
|
|
||||||
Animation<Alignment> createAnimation() {
|
|
||||||
assert(!_transitionCompleter.isCompleted,
|
|
||||||
'Cannot reuse a $runtimeType after disposing it.');
|
|
||||||
assert(_controller != null);
|
|
||||||
return AlignmentTween(begin: _initialAlignment, end: _endAlignment).animate(
|
|
||||||
CurvedAnimation(
|
|
||||||
parent: _controller,
|
|
||||||
curve: note.forwardAnimationCurve,
|
|
||||||
reverseCurve: note.reverseAnimationCurve,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Animation<double> createBlurFilterAnimation() {
|
|
||||||
if (note.modalBackdropBlur == null) return null;
|
|
||||||
|
|
||||||
return Tween(begin: 0.0, end: note.modalBackdropBlur).animate(
|
|
||||||
CurvedAnimation(
|
|
||||||
parent: _controller,
|
|
||||||
curve: Interval(
|
|
||||||
0.0,
|
|
||||||
0.35,
|
|
||||||
curve: Curves.easeInOutCirc,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Animation<Color> createColorFilterAnimation() {
|
|
||||||
if (note.modalBackgroundColor == null) return null;
|
|
||||||
|
|
||||||
return ColorTween(begin: Colors.transparent, end: note.modalBackgroundColor)
|
|
||||||
.animate(
|
|
||||||
CurvedAnimation(
|
|
||||||
parent: _controller,
|
|
||||||
curve: Interval(
|
|
||||||
0.0,
|
|
||||||
0.35,
|
|
||||||
curve: Curves.easeInOutCirc,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleStatusChanged(AnimationStatus status) {
|
|
||||||
switch (status) {
|
|
||||||
case AnimationStatus.completed:
|
|
||||||
currentStatus = NoteStatus.Visible;
|
|
||||||
_onStatusChanged?.call(currentStatus);
|
|
||||||
if (overlayEntries.isNotEmpty) overlayEntries.first.opaque = opaque;
|
|
||||||
|
|
||||||
break;
|
|
||||||
case AnimationStatus.forward:
|
|
||||||
currentStatus = NoteStatus.FadeIn;
|
|
||||||
_onStatusChanged?.call(currentStatus);
|
|
||||||
break;
|
|
||||||
case AnimationStatus.reverse:
|
|
||||||
currentStatus = NoteStatus.FadeOut;
|
|
||||||
_onStatusChanged?.call(currentStatus);
|
|
||||||
if (overlayEntries.isNotEmpty) overlayEntries.first.opaque = false;
|
|
||||||
break;
|
|
||||||
case AnimationStatus.dismissed:
|
|
||||||
assert(!overlayEntries.first.opaque);
|
|
||||||
|
|
||||||
currentStatus = NoteStatus.Dismissed;
|
|
||||||
_onStatusChanged?.call(currentStatus);
|
|
||||||
|
|
||||||
if (!isCurrent) {
|
|
||||||
navigator.finalizeRoute(this);
|
|
||||||
assert(overlayEntries.isEmpty);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
changedInternalState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void install(OverlayEntry insertionPoint) {
|
|
||||||
assert(!_transitionCompleter.isCompleted,
|
|
||||||
'Cannot install a $runtimeType after disposing it.');
|
|
||||||
_controller = createAnimationController();
|
|
||||||
assert(_controller != null,
|
|
||||||
'$runtimeType.createAnimationController() returned null.');
|
|
||||||
_filterBlurAnimation = createBlurFilterAnimation();
|
|
||||||
_filterColorAnimation = createColorFilterAnimation();
|
|
||||||
_animation = createAnimation();
|
|
||||||
assert(_animation != null, '$runtimeType.createAnimation() returned null.');
|
|
||||||
super.install(insertionPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
TickerFuture didPush() {
|
|
||||||
assert(_controller != null,
|
|
||||||
'$runtimeType.didPush called before calling install() or after calling dispose().');
|
|
||||||
assert(!_transitionCompleter.isCompleted,
|
|
||||||
'Cannot reuse a $runtimeType after disposing it.');
|
|
||||||
_animation.addStatusListener(_handleStatusChanged);
|
|
||||||
_configureTimer();
|
|
||||||
super.didPush();
|
|
||||||
return _controller.forward();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didReplace(Route<dynamic> oldRoute) {
|
|
||||||
assert(_controller != null,
|
|
||||||
'$runtimeType.didReplace called before calling install() or after calling dispose().');
|
|
||||||
assert(!_transitionCompleter.isCompleted,
|
|
||||||
'Cannot reuse a $runtimeType after disposing it.');
|
|
||||||
if (oldRoute is NoteRoute) _controller.value = oldRoute._controller.value;
|
|
||||||
_animation.addStatusListener(_handleStatusChanged);
|
|
||||||
super.didReplace(oldRoute);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool didPop(T result) {
|
|
||||||
assert(_controller != null,
|
|
||||||
'$runtimeType.didPop called before calling install() or after calling dispose().');
|
|
||||||
assert(!_transitionCompleter.isCompleted,
|
|
||||||
'Cannot reuse a $runtimeType after disposing it.');
|
|
||||||
|
|
||||||
_result = result;
|
|
||||||
_cancelTimer();
|
|
||||||
|
|
||||||
if (_wasDismissedBySwipe) {
|
|
||||||
Timer(Duration(milliseconds: 200), () {
|
|
||||||
_controller.reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
_wasDismissedBySwipe = false;
|
|
||||||
} else {
|
|
||||||
_controller.reverse();
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.didPop(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _configureTimer() {
|
|
||||||
if (note.duration != null) {
|
|
||||||
if (_timer != null && _timer.isActive) {
|
|
||||||
_timer.cancel();
|
|
||||||
}
|
|
||||||
_timer = Timer(note.duration, () {
|
|
||||||
if (this.isCurrent) {
|
|
||||||
navigator.pop();
|
|
||||||
} else if (this.isActive) {
|
|
||||||
navigator.removeRoute(this);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (_timer != null) {
|
|
||||||
_timer.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _cancelTimer() {
|
|
||||||
if (_timer != null && _timer.isActive) {
|
|
||||||
_timer.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool canTransitionTo(NoteRoute<dynamic> nextRoute) => true;
|
|
||||||
|
|
||||||
bool canTransitionFrom(NoteRoute<dynamic> previousRoute) => true;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
assert(!_transitionCompleter.isCompleted,
|
|
||||||
'Cannot dispose a $runtimeType twice.');
|
|
||||||
_controller?.dispose();
|
|
||||||
_transitionCompleter.complete(_result);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
String get debugLabel => '$runtimeType';
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class UxApp extends InheritedWidget {
|
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
UxApp({this.child});
|
|
||||||
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Stack(
|
|
||||||
children: [Positioned.fill(child: child)],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool updateShouldNotify(InheritedWidget oldWidget) => false;
|
|
||||||
|
|
||||||
UxApp of(BuildContext context) => context.dependOnInheritedWidgetOfExactType<UxApp>();
|
|
||||||
}
|
|
||||||
|
|
||||||
24
lib/ux.dart
24
lib/ux.dart
@@ -1,14 +1,11 @@
|
|||||||
library ux;
|
library ux;
|
||||||
|
|
||||||
export 'src/bend_box.dart';
|
export 'src/bend_box.dart';
|
||||||
export 'src/note.dart';
|
|
||||||
export 'src/ux_app.dart';
|
|
||||||
export 'src/json_extension.dart';
|
export 'src/json_extension.dart';
|
||||||
|
export 'src/bezier.dart';
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'src/note.dart';
|
|
||||||
|
|
||||||
class UX {
|
class UX {
|
||||||
static const MethodChannel _channel = const MethodChannel('ux');
|
static const MethodChannel _channel = const MethodChannel('ux');
|
||||||
@@ -17,21 +14,4 @@ class UX {
|
|||||||
final String version = await _channel.invokeMethod('getPlatformVersion');
|
final String version = await _channel.invokeMethod('getPlatformVersion');
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension UxExtension on BuildContext {
|
|
||||||
showText<T>(String text,
|
|
||||||
{bool modal = false,
|
|
||||||
double backdropBlur = 0.0,
|
|
||||||
Duration duration = const Duration(seconds: 3)}) =>
|
|
||||||
Note<T>(
|
|
||||||
duration: duration,
|
|
||||||
modalBackdropBlur: backdropBlur,
|
|
||||||
isModal: modal,
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(color: Color(0xFF202020)),
|
|
||||||
child: ListTile(
|
|
||||||
title:
|
|
||||||
Text(text, style: TextStyle(color: Colors.white)))))
|
|
||||||
.show(this);
|
|
||||||
}
|
|
||||||
31
pubspec.lock
31
pubspec.lock
@@ -7,42 +7,42 @@ packages:
|
|||||||
name: archive
|
name: archive
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.11"
|
version: "2.0.13"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: args
|
name: args
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.2"
|
version: "1.6.0"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: async
|
name: async
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.0"
|
version: "2.4.1"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: boolean_selector
|
name: boolean_selector
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "2.0.0"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: charcode
|
name: charcode
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.2"
|
version: "1.1.3"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.14.11"
|
version: "1.14.12"
|
||||||
convert:
|
convert:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -56,7 +56,7 @@ packages:
|
|||||||
name: crypto
|
name: crypto
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.4"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -73,7 +73,7 @@ packages:
|
|||||||
name: image
|
name: image
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.1.12"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -95,13 +95,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.6.4"
|
version: "1.6.4"
|
||||||
pedantic:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: pedantic
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.8.0+1"
|
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -115,7 +108,7 @@ packages:
|
|||||||
name: quiver
|
name: quiver
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.5"
|
version: "2.1.3"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -127,7 +120,7 @@ packages:
|
|||||||
name: source_span
|
name: source_span
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.5"
|
version: "1.7.0"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -162,7 +155,7 @@ packages:
|
|||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.11"
|
version: "0.2.15"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -183,6 +176,6 @@ packages:
|
|||||||
name: xml
|
name: xml
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.5.0"
|
version: "3.6.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.6.0 <3.0.0"
|
dart: ">=2.6.0 <3.0.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user