morphing_sheet 0.1.0 copy "morphing_sheet: ^0.1.0" to clipboard
morphing_sheet: ^0.1.0 copied to clipboard

A production-ready multi-stage, gesture-driven, spatially continuous UX sheet with physics-based snapping and morphing fullscreen transitions.

morphing_sheet #

A production-ready Flutter package implementing a multi-stage, gesture-driven, spatially continuous UX sheet with physics-based snapping and morphing fullscreen transitions.

Features #

  • Three-stage sheet -- collapsed (25%), half-expanded (60%), fullscreen (100%) with smooth continuous transitions between them.
  • Physics-based gestures -- velocity-aware snap resolution, rubber-band resistance at bounds, and configurable flick thresholds.
  • Single animation source of truth -- one AnimationController drives all visual properties (height, radius, elevation, blur, scale) preventing rebuild storms.
  • State-management agnostic -- SheetController extends ChangeNotifier, works with Provider, Riverpod, Bloc, or vanilla ListenableBuilder.
  • Fully customizable -- snap points, curves, radii, elevation, background effects, and physics are all configurable through SheetConfig.
  • Horizontal page switching -- built-in PageView with per-snap-point control over whether swiping is enabled.
  • Injectable physics -- extend SheetPhysics to implement magnetic snap points, spring simulations, or platform-specific motion.
  • Performance optimized -- RepaintBoundary isolation, ListenableBuilder for targeted rebuilds, no setState during animations.

Installation #

dependencies:
  morphing_sheet:
    path: ../  # or from pub once published

Quick Start #

import 'package:morphing_sheet/morphing_sheet.dart';

class MyPage extends StatefulWidget {
  @override
  State<MyPage> createState() => _MyPageState();
}

class _MyPageState extends State<MyPage>
    with SingleTickerProviderStateMixin {
  late final SheetController _controller;

  @override
  void initState() {
    super.initState();
    _controller = SheetController(vsync: this);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: MorphingSheet(
        controller: _controller,
        pageCount: 3,
        headerBuilder: (context, progress, state) {
          return Container(
            padding: const EdgeInsets.all(16),
            child: Text('Progress: ${(progress * 100).toInt()}%'),
          );
        },
        contentBuilder: (context, index, progress, state) {
          return Center(child: Text('Page $index'));
        },
        child: const Center(child: Text('Background')),
      ),
    );
  }
}

Architecture #

lib/
 ├── morphing_sheet.dart           # barrel export
 └── src/
      ├── core/
      │    └── sheet_physics.dart   # abstract physics interface
      ├── physics/
      │    ├── default_sheet_physics.dart
      │    └── snap_resolver.dart
      ├── controllers/
      │    └── sheet_controller.dart
      ├── models/
      │    ├── sheet_state.dart
      │    ├── sheet_config.dart
      │    ├── sheet_visual_state.dart
      │    └── snap_point.dart
      ├── animations/
      │    ├── curve_presets.dart
      │    ├── sheet_tween.dart
      │    └── transition_spec.dart
      └── widgets/
           ├── morphing_sheet_widget.dart
           ├── sheet_scaffold.dart
           ├── gesture_layer.dart
           ├── background_transform.dart
           └── sheet_page_view.dart

Layer responsibilities #

Layer Purpose
Models Immutable state (SheetState), configuration (SheetConfig), value objects (SnapPoint), sealed state hierarchy (SheetVisualState)
Core Abstract interfaces (SheetPhysics) for dependency inversion
Physics Concrete physics implementations, pure-function snap resolution
Controllers SheetController -- MVVM ViewModel, owns AnimationController, exposes immutable state snapshots
Animations Interpolation helpers (SheetTween), curve presets (SheetCurves), per-property timing (TransitionSpec)
Widgets Presentation layer -- MorphingSheet, SheetScaffold, GestureLayer, BackgroundTransform, SheetPageView

Configuration #

const config = SheetConfig(
  snapPoints: [
    SnapPoint(position: 0.2, label: 'collapsed'),
    SnapPoint(position: 0.5, label: 'half'),
    SnapPoint(position: 1.0, label: 'full', enableHorizontalSwipe: false),
  ],
  cornerRadius: 28,
  elevation: 12,
  backgroundMinScale: 0.92,
  backgroundMaxBlur: 8,
  expandCurve: SheetCurves.expand,
  collapseCurve: SheetCurves.collapse,
);

Custom Physics #

class MagneticPhysics extends SheetPhysics {
  const MagneticPhysics();

  @override
  double applyResistance(double delta, double pos, double min, double max) {
    // Stronger pull near snap points
    return delta * 0.8;
  }

  @override
  SnapPoint resolveSnap(double pos, double velocity, List<SnapPoint> points) {
    return SnapResolver.resolve(pos, velocity, points, flickThreshold: 0.5);
  }

  @override
  bool shouldFlick(double velocity) => velocity.abs() > 0.5;
}

MorphingSheet(physics: const MagneticPhysics(), ...)

Visual State Matching #

switch (controller.visualState) {
  case CollapsedState():
    // show minimal UI
  case HalfExpandedState():
    // show richer content
  case ExpandedState():
    // show full detail
  case TransitioningState(:final from, :final to, :final localProgress):
    // animate between states
}

Minimum Requirements #

  • Flutter >= 3.10.0
  • Dart >= 3.10.4

License #

MIT

3
likes
0
points
223
downloads

Publisher

unverified uploader

Weekly Downloads

A production-ready multi-stage, gesture-driven, spatially continuous UX sheet with physics-based snapping and morphing fullscreen transitions.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter

More

Packages that depend on morphing_sheet