pub.dev score: license, dartdoc, analysis, tests
- Add MIT license - Add dartdoc to all public APIs (UxKeyboard, BendBox, Bezier, Json) - Add analysis_options.yaml with flutter_lints and public_member_api_docs - Add repository, issue_tracker, topics to pubspec.yaml - Expand package description - Fix BendBox constructor (const, super.key) - Remove unused import in keyboard.dart - Replace boilerplate tests with UxKeyboard smoke tests - Move ux dependency to example's dependencies (not dev_dependencies)
This commit is contained in:
22
LICENSE
22
LICENSE
@@ -1 +1,21 @@
|
||||
TODO: Add your license here.
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 SWIPELAB LTD
|
||||
|
||||
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.
|
||||
|
||||
5
analysis_options.yaml
Normal file
5
analysis_options.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
linter:
|
||||
rules:
|
||||
public_member_api_docs: true
|
||||
@@ -1 +1 @@
|
||||
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"ux","path":"/Users/agra/projects/ux/","native_build":true,"dependencies":[],"dev_dependency":true}],"android":[{"name":"ux","path":"/Users/agra/projects/ux/","native_build":true,"dependencies":[],"dev_dependency":true}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"ux","dependencies":[]}],"date_created":"2026-04-15 13:13:36.363037","version":"3.41.5","swift_package_manager_enabled":{"ios":false,"macos":false}}
|
||||
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"ux","path":"/Users/agra/projects/ux/","native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"ux","path":"/Users/agra/projects/ux/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"ux","dependencies":[]}],"date_created":"2026-04-16 18:49:44.385595","version":"3.41.5","swift_package_manager_enabled":{"ios":false,"macos":false}}
|
||||
@@ -62,6 +62,14 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@@ -91,6 +99,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -177,12 +193,12 @@ packages:
|
||||
source: hosted
|
||||
version: "0.7.10"
|
||||
ux:
|
||||
dependency: "direct dev"
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: ".."
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.2.0"
|
||||
version: "0.3.0"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -8,56 +8,14 @@ environment:
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: 1.0.8
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
ux:
|
||||
path: ../
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
dev_dependencies:
|
||||
flutter_lints: ^6.0.0
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
# The following section is specific to Flutter.
|
||||
flutter:
|
||||
|
||||
# The following line ensures that the Material Icons font is
|
||||
# included with your application, so that you can use the icons in
|
||||
# the material Icons class.
|
||||
uses-material-design: true
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/assets-and-images/#resolution-aware.
|
||||
|
||||
# For details regarding adding assets from package dependencies, see
|
||||
# https://flutter.dev/assets-and-images/#from-packages
|
||||
|
||||
# To add custom fonts to your application, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
# - asset: fonts/Schyler-Italic.ttf
|
||||
# style: italic
|
||||
# - family: Trajan Pro
|
||||
# fonts:
|
||||
# - asset: fonts/TrajanPro.ttf
|
||||
# - asset: fonts/TrajanPro_Bold.ttf
|
||||
# weight: 700
|
||||
#
|
||||
# For details regarding fonts from package dependencies,
|
||||
# see https://flutter.dev/custom-fonts/#from-packages
|
||||
|
||||
@@ -1,26 +1,11 @@
|
||||
// This is a basic Flutter widget test.
|
||||
//
|
||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||
// utility that Flutter provides. For example, you can send tap and scroll
|
||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||
// tree, read text, and verify that the values of widget properties are correct.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:ux_example/main.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Verify Platform version', (WidgetTester tester) async {
|
||||
// Build our app and trigger a frame.
|
||||
await tester.pumpWidget(MyApp());
|
||||
|
||||
// Verify that platform version is retrieved.
|
||||
expect(
|
||||
find.byWidgetPredicate(
|
||||
(Widget widget) =>
|
||||
widget is Text && (widget.data?.startsWith('Running on:') ?? false),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
testWidgets('ChatScreen renders', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(MaterialApp(home: ChatScreen()));
|
||||
expect(find.text('UxKeyboard Chat'), findsOneWidget);
|
||||
expect(find.text('Type a message...'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// A widget that paints a filled shape with curved (bent) edges.
|
||||
///
|
||||
/// Each edge bends inward by the amount specified in [inward].
|
||||
class BendBox extends StatelessWidget {
|
||||
/// How far each edge bends inward. Positive values bend toward the center.
|
||||
final EdgeInsets inward;
|
||||
|
||||
/// The fill color of the shape.
|
||||
final Color color;
|
||||
|
||||
BendBox({this.inward = const EdgeInsets.all(0), this.color = Colors.red});
|
||||
/// Creates a [BendBox] with the given [inward] bend and [color].
|
||||
const BendBox({super.key, this.inward = const EdgeInsets.all(0), this.color = Colors.red});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CustomPaint(painter: _BendBoxPainter(inward: inward, color: color));
|
||||
}
|
||||
@@ -20,6 +28,7 @@ class _BendBoxPainter extends CustomPainter {
|
||||
required this.color,
|
||||
});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final paint = Paint()
|
||||
..style = PaintingStyle.fill
|
||||
@@ -39,5 +48,6 @@ class _BendBoxPainter extends CustomPainter {
|
||||
canvas.drawPath(path, paint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(CustomPainter oldDelegate) => true;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:math';
|
||||
import 'dart:ui';
|
||||
|
||||
/// Computes the approximate arc length of a [Bezier] curve by sampling.
|
||||
double bezierLength(Bezier bezier, [double steps = 10]) {
|
||||
assert(steps != 0);
|
||||
final step = 1 / steps;
|
||||
@@ -14,36 +15,56 @@ double bezierLength(Bezier bezier, [double steps = 10]) {
|
||||
return length;
|
||||
}
|
||||
|
||||
/// Base class for parametric bezier curves.
|
||||
///
|
||||
/// Evaluate a point on the curve with [point] at parameter `t` in [0, 1].
|
||||
abstract class Bezier {
|
||||
/// Returns the point on the curve at parameter [t] (0 = start, 1 = end).
|
||||
Offset point(double t);
|
||||
|
||||
/// The approximate arc length of the curve.
|
||||
double get length => bezierLength(this);
|
||||
}
|
||||
|
||||
/// A straight line segment from [p0] to [p1].
|
||||
class LinearBezier extends Bezier {
|
||||
Offset p0, p1;
|
||||
/// The start point.
|
||||
Offset p0;
|
||||
|
||||
/// The end point.
|
||||
Offset p1;
|
||||
|
||||
/// Creates a linear bezier from [p0] to [p1].
|
||||
LinearBezier(this.p0, this.p1);
|
||||
|
||||
@override
|
||||
Offset point(double t) {
|
||||
return p0 + (p1 - p0) * t;
|
||||
}
|
||||
|
||||
@override
|
||||
double get length => (p1 - p0).distance;
|
||||
}
|
||||
|
||||
/// A quadratic bezier curve with one control point.
|
||||
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;
|
||||
/// The start point.
|
||||
Offset p0;
|
||||
|
||||
/// The control point.
|
||||
Offset p1;
|
||||
|
||||
/// The end point.
|
||||
Offset p2;
|
||||
|
||||
/// Creates a quadratic bezier from [p0] to [p2] with control point [p1].
|
||||
QuadraticBezier(this.p0, this.p1, this.p2);
|
||||
|
||||
@override
|
||||
Offset point(double t) {
|
||||
return Offset(
|
||||
_quadraticBezier(t, p0.dx, p1.dx, p2.dx),
|
||||
@@ -52,6 +73,7 @@ class QuadraticBezier extends Bezier {
|
||||
}
|
||||
}
|
||||
|
||||
/// A cubic bezier curve with two control points.
|
||||
class CubicBezier extends Bezier {
|
||||
double _cubicBezier(double t, double p0, double p1, double p2, double p3) {
|
||||
return pow(1 - t, 3) * p0 +
|
||||
@@ -60,35 +82,53 @@ class CubicBezier extends Bezier {
|
||||
pow(t, 3) * p3;
|
||||
}
|
||||
|
||||
Offset p0, p1, p2, p3;
|
||||
/// The start point.
|
||||
Offset p0;
|
||||
|
||||
/// The first control point.
|
||||
Offset p1;
|
||||
|
||||
/// The second control point.
|
||||
Offset p2;
|
||||
|
||||
/// The end point.
|
||||
Offset p3;
|
||||
|
||||
/// Creates a cubic bezier from [p0] to [p3] with control points [p1] and [p2].
|
||||
CubicBezier(this.p0, this.p1, this.p2, this.p3);
|
||||
|
||||
Offset point(double t) =>
|
||||
Offset(
|
||||
@override
|
||||
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),
|
||||
);
|
||||
}
|
||||
|
||||
/// A composite path of multiple bezier segments.
|
||||
///
|
||||
/// Build a path incrementally with [lineTo], [quadTo], and [cubeTo].
|
||||
/// Evaluate any point along the total path with [point].
|
||||
class PathBezier extends Bezier {
|
||||
double _length = 0;
|
||||
|
||||
@override
|
||||
double get length => _length;
|
||||
|
||||
List<Bezier> _curves = [];
|
||||
List<double> _lens = [];
|
||||
final List<Bezier> _curves = [];
|
||||
final List<double> _lens = [];
|
||||
|
||||
/// The starting point of the path.
|
||||
final Offset p0;
|
||||
Offset _p0;
|
||||
|
||||
/// Creates a path starting at [p0].
|
||||
PathBezier(this.p0) : _p0 = p0;
|
||||
|
||||
/// Creates a path tracing a rounded rectangle.
|
||||
static PathBezier roundedRect(RRect rrect) {
|
||||
return PathBezier(Offset(rrect.left + rrect.width / 2, rrect.top))
|
||||
..lineTo(Offset(rrect.right - rrect.trRadiusX, rrect.top))
|
||||
..quadTo(
|
||||
Offset(rrect.right, rrect.top),
|
||||
..quadTo(Offset(rrect.right, rrect.top),
|
||||
Offset(rrect.right, rrect.top + rrect.trRadiusY))
|
||||
..lineTo(Offset(rrect.right, rrect.bottom - rrect.brRadiusY))
|
||||
..quadTo(Offset(rrect.right, rrect.bottom),
|
||||
@@ -102,7 +142,7 @@ class PathBezier extends Bezier {
|
||||
..lineTo(Offset(rrect.left + rrect.width / 2, rrect.top));
|
||||
}
|
||||
|
||||
_add(Bezier bezier, Offset pn) {
|
||||
void _add(Bezier bezier, Offset pn) {
|
||||
final bl = bezierLength(bezier);
|
||||
_curves.add(bezier);
|
||||
_lens.add(bl);
|
||||
@@ -110,32 +150,29 @@ class PathBezier extends Bezier {
|
||||
_p0 = pn;
|
||||
}
|
||||
|
||||
lineTo(Offset p1) => _add(LinearBezier(_p0, p1), p1);
|
||||
/// Appends a straight line to [p1].
|
||||
void lineTo(Offset p1) => _add(LinearBezier(_p0, p1), p1);
|
||||
|
||||
quadTo(Offset p1, Offset p2) => _add(QuadraticBezier(_p0, p1, p2), p2);
|
||||
/// Appends a quadratic curve with control point [p1] to endpoint [p2].
|
||||
void quadTo(Offset p1, Offset p2) =>
|
||||
_add(QuadraticBezier(_p0, p1, p2), p2);
|
||||
|
||||
cubeTo(Offset p1, Offset p2, Offset p3) =>
|
||||
/// Appends a cubic curve with control points [p1], [p2] to endpoint [p3].
|
||||
void cubeTo(Offset p1, Offset p2, Offset p3) =>
|
||||
_add(CubicBezier(_p0, p1, p2, p3), p3);
|
||||
|
||||
relativeLineTo(Offset p1) =>
|
||||
lineTo(
|
||||
p1 + _p0,
|
||||
);
|
||||
/// Appends a straight line to a point relative to the current position.
|
||||
void 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,
|
||||
);
|
||||
/// Appends a quadratic curve with relative control and end points.
|
||||
void relativeQuadTo(Offset p1, Offset p2) =>
|
||||
quadTo(p1 + p0, p1 + p2 + p0);
|
||||
|
||||
/// Appends a cubic curve with relative control and end points.
|
||||
void relativeCubeTo(Offset p1, Offset p2, Offset p3) =>
|
||||
cubeTo(p0 + p1, p0 + p1 + p2, p0 + p1 + p2 + p3);
|
||||
|
||||
@override
|
||||
Offset point(double t) {
|
||||
if (t > 1) {
|
||||
t = t - t.floor();
|
||||
|
||||
@@ -1,15 +1,28 @@
|
||||
typedef T _JsonConvert<T>(Map<String, dynamic> json);
|
||||
/// Signature for a function that converts a JSON map to a typed object.
|
||||
typedef JsonConvert<T> = T Function(Map<String, dynamic> json);
|
||||
|
||||
/// Utilities for converting JSON structures to typed Dart objects.
|
||||
class Json {
|
||||
static List<T> list<T>(List? json, _JsonConvert<T> fromJson) => json == null
|
||||
/// Converts a JSON list of maps to a typed `List<T>`.
|
||||
///
|
||||
/// Returns an empty list if [json] is null.
|
||||
static List<T> list<T>(List? json, JsonConvert<T> fromJson) => json == null
|
||||
? []
|
||||
: json.cast<Map<String, dynamic>>().map(fromJson).toList();
|
||||
|
||||
static Map<String, T> map<T>(Map? json, _JsonConvert<T> fromJson) => json == null
|
||||
? {}
|
||||
: Map.fromEntries(
|
||||
json.entries.map((e) => MapEntry(e.key, fromJson(e.value))));
|
||||
/// Converts a JSON map of maps to a typed `Map<String, T>`.
|
||||
///
|
||||
/// Returns an empty map if [json] is null.
|
||||
static Map<String, T> map<T>(Map? json, JsonConvert<T> fromJson) =>
|
||||
json == null
|
||||
? {}
|
||||
: Map.fromEntries(
|
||||
json.entries.map((e) => MapEntry(e.key, fromJson(e.value))));
|
||||
|
||||
/// Reads a value at a dot-separated [path] from a nested JSON structure.
|
||||
///
|
||||
/// Supports both map keys and list indices (numeric segments).
|
||||
/// Returns [defaultValue] if the path doesn't exist.
|
||||
static dynamic path<T>(Map<String, dynamic> json, String path,
|
||||
{dynamic defaultValue}) {
|
||||
try {
|
||||
@@ -24,7 +37,6 @@ class Json {
|
||||
});
|
||||
return current ?? defaultValue;
|
||||
} catch (error) {
|
||||
print(error);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/animation.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
|
||||
@@ -126,19 +125,50 @@ double _inverseLerp(List<double> samples, double value) {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Frame-accurate keyboard height tracking for iOS and Android.
|
||||
///
|
||||
/// Reads the keyboard position directly from the native layer via FFI each
|
||||
/// frame, bypassing Flutter's `MediaQuery.viewInsets` which lags behind.
|
||||
///
|
||||
/// Supports interactive dismiss (swipe-to-dismiss like iMessage) and provides
|
||||
/// sampled native animation curves with adaptive learning.
|
||||
///
|
||||
/// Use the singleton [instance] and listen for changes via [addListener]:
|
||||
///
|
||||
/// ```dart
|
||||
/// final keyboard = UxKeyboard.instance;
|
||||
/// keyboard.enableInteractiveDismiss(trackingInset: 56);
|
||||
/// ```
|
||||
class UxKeyboard with ChangeNotifier {
|
||||
UxKeyboard._() {
|
||||
if (_lib == null) return;
|
||||
SchedulerBinding.instance.addPersistentFrameCallback(_onFrame);
|
||||
}
|
||||
|
||||
/// The singleton instance.
|
||||
static final UxKeyboard instance = UxKeyboard._();
|
||||
|
||||
double _height = 0;
|
||||
|
||||
/// The current keyboard height in logical pixels.
|
||||
///
|
||||
/// Updated every frame while the keyboard is animating or open.
|
||||
/// Returns 0 when the keyboard is fully closed.
|
||||
double get height => _height;
|
||||
|
||||
/// The last system-reported keyboard height.
|
||||
///
|
||||
/// Unlike [height], this is not interpolated — it reflects the target
|
||||
/// height from the most recent keyboard notification.
|
||||
double get systemHeight => _uxSystemHeight?.call() ?? 0;
|
||||
|
||||
/// Whether the keyboard is currently visible.
|
||||
bool get isOpen => _height > 0;
|
||||
|
||||
/// Whether an interactive dismiss pan gesture is active.
|
||||
///
|
||||
/// When true, the user is dragging the keyboard down. Use this to freeze
|
||||
/// scroll views so they don't fight the pan gesture.
|
||||
bool get isTracking => (_uxIsTracking?.call() ?? 0) > 0;
|
||||
|
||||
// Animation state — replays the keyboard's own animation inside Flutter.
|
||||
@@ -308,6 +338,13 @@ class UxKeyboard with ChangeNotifier {
|
||||
_obs.clear();
|
||||
}
|
||||
|
||||
/// Enables swipe-to-dismiss on the keyboard.
|
||||
///
|
||||
/// [trackingInset] is the height of your input bar in logical pixels.
|
||||
/// The dismiss gesture activates when the finger enters the keyboard zone
|
||||
/// below this inset.
|
||||
void enableInteractiveDismiss({double trackingInset = 0}) => _uxEnableInteractiveDismiss?.call(trackingInset);
|
||||
|
||||
/// Disables the swipe-to-dismiss gesture.
|
||||
void disableInteractiveDismiss() => _uxDisableInteractiveDismiss?.call();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
int _idCounter = 0;
|
||||
|
||||
/// Returns a monotonically increasing integer ID.
|
||||
///
|
||||
/// Useful for generating unique keys within a single isolate session.
|
||||
/// Resets to 0 on app restart.
|
||||
int nextId() => _idCounter++;
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
library ux;
|
||||
/// Flutter toolkit for fluid, native-feeling UIs.
|
||||
///
|
||||
/// Includes [UxKeyboard] for frame-accurate keyboard height tracking,
|
||||
/// [BendBox] for curved layout painting, and bezier curve utilities.
|
||||
library;
|
||||
|
||||
export 'src/bend_box.dart';
|
||||
export 'src/json_extension.dart';
|
||||
|
||||
16
pubspec.lock
16
pubspec.lock
@@ -54,6 +54,14 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@@ -83,6 +91,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
12
pubspec.yaml
12
pubspec.yaml
@@ -1,7 +1,16 @@
|
||||
name: ux
|
||||
description: UX Kit
|
||||
description: >-
|
||||
Flutter toolkit for fluid, native-feeling UIs. Includes frame-accurate
|
||||
keyboard height tracking via FFI with interactive dismiss, bezier utilities,
|
||||
and layout primitives.
|
||||
version: 0.3.0
|
||||
homepage: https://swipelab.co/ux.html
|
||||
repository: https://github.com/swipelab/ux
|
||||
issue_tracker: https://github.com/swipelab/ux/issues
|
||||
topics:
|
||||
- keyboard
|
||||
- ui
|
||||
- animation
|
||||
|
||||
environment:
|
||||
sdk: ">=3.0.0"
|
||||
@@ -11,6 +20,7 @@ dependencies:
|
||||
sdk: flutter
|
||||
|
||||
dev_dependencies:
|
||||
flutter_lints: ^6.0.0
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:ux/ux.dart';
|
||||
|
||||
void main() {
|
||||
const MethodChannel channel = MethodChannel('ux');
|
||||
|
||||
setUp(() {
|
||||
channel.setMockMethodCallHandler((MethodCall methodCall) async {
|
||||
return '42';
|
||||
});
|
||||
test('UxKeyboard.instance is a singleton', () {
|
||||
expect(UxKeyboard.instance, same(UxKeyboard.instance));
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
channel.setMockMethodCallHandler(null);
|
||||
test('UxKeyboard.height starts at 0', () {
|
||||
expect(UxKeyboard.instance.height, 0);
|
||||
});
|
||||
|
||||
test('getPlatformVersion', () async {
|
||||
expect(await UX.platformVersion, '42');
|
||||
test('UxKeyboard.isOpen is false when height is 0', () {
|
||||
expect(UxKeyboard.instance.isOpen, false);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user