Skip to main content

fold_transition 0.1.1 copy "fold_transition: ^0.1.1" to clipboard
fold_transition: ^0.1.1 copied to clipboard

PlatformAndroid

Fold transitions for Android foldables. Tracks a continuous hinge angle when the sensor allows it, and plays a fixed-duration animation on quantized hardware such as Galaxy Z Fold.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:fold_transition/fold_transition.dart';

void main() {
  foldLogEnabled = true;
  foldLog('boot', 'logging enabled');
  runApp(const FoldTransitionExampleApp());
}

/// Demo gallery that plays a fold effect on posture change or Trigger.
class FoldTransitionExampleApp extends StatelessWidget {
  /// Creates the example app.
  const FoldTransitionExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData.dark(),
      home: const FoldTransitionExamplePage(),
    );
  }
}

/// Hosts a [FoldAnimator] and a tile grid wrapped in [FoldTransition].
class FoldTransitionExamplePage extends StatefulWidget {
  /// Creates the example page.
  const FoldTransitionExamplePage({super.key});

  @override
  State<FoldTransitionExamplePage> createState() =>
      _FoldTransitionExamplePageState();
}

class _FoldTransitionExamplePageState extends State<FoldTransitionExamplePage>
    with TickerProviderStateMixin {
  late FoldAnimator _animator;
  var _preference = FoldModePreference.auto;
  final _blur = const BlurDissolveEffect();
  final _bend = BendEffect();
  var _useBend = false;
  var _hasSnapshot = false;

  static const _palette = [
    Color(0xFFE57373),
    Color(0xFF64B5F6),
    Color(0xFF81C784),
    Color(0xFFFFB74D),
    Color(0xFFBA68C8),
    Color(0xFF4DB6AC),
    Color(0xFFFF8A65),
    Color(0xFF90A4AE),
  ];

  @override
  void initState() {
    super.initState();
    _animator = _createAnimator();
  }

  FoldAnimator _createAnimator() {
    final animator = FoldAnimator(
      vsync: this,
      source: SensorHingeSource(),
      preference: _preference,
    );
    animator.addListener(_rebuild);
    return animator;
  }

  void _setPreference(FoldModePreference preference) {
    if (preference == _preference) return;
    final old = _animator;
    old.removeListener(_rebuild);
    _preference = preference;
    final next = _createAnimator();
    setState(() {
      _hasSnapshot = false;
      _animator = next;
    });
    old.dispose();
  }

  @override
  void dispose() {
    _animator.removeListener(_rebuild);
    _animator.dispose();
    super.dispose();
  }

  void _rebuild() {
    if (mounted) setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    final capability = _animator.capability;
    return Scaffold(
      backgroundColor: const Color(0xFF111111),
      body: FoldTransition(
        animator: _animator,
        effect: _useBend ? _bend : _blur,
        onSnapshotChanged: (held) {
          if (mounted) setState(() => _hasSnapshot = held);
        },
        child: GridView.builder(
          padding: const EdgeInsets.fromLTRB(12, 12, 12, 120),
          gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 4,
            mainAxisSpacing: 8,
            crossAxisSpacing: 8,
          ),
          itemCount: 24,
          itemBuilder: (context, index) {
            return Container(
              color: _palette[index % _palette.length],
              alignment: Alignment.center,
              child: Text(
                'Tile ${index + 1}',
                style: const TextStyle(
                  color: Colors.white,
                  fontWeight: FontWeight.w600,
                ),
              ),
            );
          },
        ),
      ),
      bottomNavigationBar: Material(
        color: const Color(0xFF1C1C1C),
        child: SafeArea(
          top: false,
          child: Padding(
            padding: const EdgeInsets.fromLTRB(12, 8, 12, 8),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                Text(
                  _animator.mode.name.toUpperCase(),
                  style: const TextStyle(
                    color: Colors.white,
                    fontSize: 16,
                    fontWeight: FontWeight.w600,
                    letterSpacing: 1.2,
                  ),
                ),
                const SizedBox(height: 4),
                Text(
                  'nInt=${_animator.intermediateCount}  ·  '
                  '${_animator.posture.name}  ·  '
                  '${_animator.degrees.toStringAsFixed(1)}°  ·  '
                  'sensor=${capability.hasAngleSensor}  ·  '
                  'max=${capability.maxDegrees}  ·  '
                  'v=${_animator.animation.value.toStringAsFixed(2)}  ·  '
                  'snap=${_hasSnapshot ? 'held' : 'none'}',
                  textAlign: TextAlign.center,
                  style: TextStyle(
                    color: Colors.white.withValues(alpha: 0.55),
                    fontSize: 11,
                    fontFamily: 'monospace',
                  ),
                ),
                const SizedBox(height: 8),
                SegmentedButton<FoldModePreference>(
                  segments: const [
                    ButtonSegment(
                      value: FoldModePreference.auto,
                      label: Text('Auto'),
                    ),
                    ButtonSegment(
                      value: FoldModePreference.forceTracked,
                      label: Text('Tracked'),
                    ),
                    ButtonSegment(
                      value: FoldModePreference.forceTriggered,
                      label: Text('Triggered'),
                    ),
                  ],
                  selected: {_preference},
                  onSelectionChanged: (value) => _setPreference(value.first),
                ),
                const SizedBox(height: 8),
                Row(
                  children: [
                    FilledButton(
                      onPressed: _animator.debugTrigger,
                      child: const Text('Trigger'),
                    ),
                    const SizedBox(width: 12),
                    Expanded(
                      child: SegmentedButton<bool>(
                        segments: const [
                          ButtonSegment(value: false, label: Text('Blur')),
                          ButtonSegment(value: true, label: Text('Bend')),
                        ],
                        selected: {_useBend},
                        onSelectionChanged: (value) {
                          setState(() => _useBend = value.first);
                        },
                      ),
                    ),
                  ],
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}
0
likes
160
points
227
downloads
screenshot

Documentation

API reference

Publisher

verified publishertechlapse.co.in

Weekly Downloads

Fold transitions for Android foldables. Tracks a continuous hinge angle when the sensor allows it, and plays a fixed-duration animation on quantized hardware such as Galaxy Z Fold.

Repository (GitHub)
View/report issues

Topics

#foldable #animation #hinge #android

License

MIT (license)

Dependencies

flutter

More

Packages that depend on fold_transition

Packages that implement fold_transition