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.

fold_transition #

pub package license

Fold transitions for Android foldables. Android is the only supported platform. The package picks a driver at runtime:

  • Tracked — animation follows a continuous hinge angle (Pixel Fold, Surface Duo).
  • Triggered — a settled posture change plays a fixed-duration animation (Samsung Galaxy Z Fold). TYPE_HINGE_ANGLE there only reports 0 / 90 / 180. The finer Samsung vendor sensor needs a signature-level permission that third-party apps cannot obtain.

Both modes expose the same Animation<double> (0 at rest, 1 fully transitioned). Effect code does not branch on mode.

Mode is detected empirically from readings. Advertised sensor precision is not trusted — a device can claim fine resolution and still emit only three values.


Install #

In the consumer app pubspec.yaml (not this plugin's):

dependencies:
  fold_transition: ^0.1.1

Then:

flutter pub get

Until a hosted version is available, depend on git:

dependencies:
  fold_transition:
    git:
      url: https://github.com/techlapse/fold_transition.git
      ref: main

Required: Android manifest #

A plugin cannot inject this into the consumer's manifest. Without it, Android recreates the Activity on unfold and there is no continuous state left to animate.

In android/app/src/main/AndroidManifest.xml on the launcher activity:

<activity
    android:name=".MainActivity"
    android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize|keyboard|keyboardHidden|uiMode|density|locale|fontScale|layoutDirection"
    android:resizeableActivity="true"
    ... >

The example app already has this. Copy it into every real host app.

Minimum Android for live hinge angle: API 30 (Sensor.TYPE_HINGE_ANGLE). The plugin still loads on older APIs; HingeCapability.hasAngleSensor is false and you drive the effect with debugTrigger() or MockHingeSource.


Usage #

class _InboxState extends State<Inbox> with SingleTickerProviderStateMixin {
  late final FoldAnimator _animator = FoldAnimator(vsync: this);

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

  @override
  Widget build(BuildContext context) {
    return FoldTransition(
      animator: _animator,
      effect: const BlurDissolveEffect(),
      child: InboxBody(folder: widget.folder),
    );
  }
}

If the host State ever creates more than one AnimationController (for example recreating FoldAnimator when the user toggles mode), use TickerProviderStateMixin instead of SingleTickerProviderStateMixin.

Force a driver #

FoldAnimator(
  vsync: this,
  preference: FoldModePreference.forceTriggered, // or forceTracked
);

auto (default) starts triggered and promotes to tracked after three intermediate hinge readings in the same session. It never demotes.

Tests and debugTrigger #

Widget tests and animator.debugTrigger() do not need a hinge. Live angle and posture still require an Android device with TYPE_HINGE_ANGLE (API 30+).


Effects #

Effect What it does
BlurDissolveEffect Outgoing snapshot scales up, blurs, and fades.
BendEffect Crease-bend via shaders/bend.frag.
AppleDuoEffect Cover-to-inner glass handoff.

Pass the effect into FoldTransition. Effects consume animation.value as transition amount: 0 at rest, 1 fully transitioned.


Example app #

cd example
flutter run -d RZCY61H3BLP   # or any attached Android foldable

The example shows live mode, intermediate count, posture, degrees, sensor capability, animation value, and whether a snapshot is held. Segmented Auto / Tracked / Triggered rebuilds the animator so you can compare drivers on one device. Trigger calls debugTrigger().

On a Galaxy Z Fold, force Tracked once: the animation snaps between 0 / 90 / 180. That is the public sensor, and why triggered is the default.

Stay on the inner display. Folding to the cover stops the inner Activity (WindowStopped); Dart is paused and posture changes in that window are missed.


Device support #

Device family Public hinge angle Resulting mode
Samsung Galaxy Z Fold / Flip Discrete 0 / 90 / 180 Triggered
Pixel Fold Continuous Tracked (after 3 intermediate readings)
Surface Duo Continuous (historically 0–360°) Tracked
Android emulator (no hinge) None Triggered — use Trigger or MockHingeSource

iOS, web, Windows, macOS, and Linux are not supported. This plugin registers on Android only.


Logging #

Disabled by default (foldLogEnabled = false). The example sets it true in main().

adb logcat -s flutter | grep 'fold_transition\.'

Do not grep fold_transition alone — that matches the Android package name and floods WindowManager noise.


License #

MIT. See LICENSE.


Publishing to pub.dev #

This section is the complete setup for maintainers shipping fold_transition to pub.dev. Publishing is permanent: you cannot delete a version. You can retract within 7 days (it still exists and still occupies that version number).

Official docs: Publishing packages.

Publish from the plugin root, never from example/ #

example/ is a demo app. It has publish_to: 'none', no LICENSE, no version, and a path dependency on this plugin. That is correct. Do not remove publish_to, and do not run publish there.

If you see A private package cannot be published, missing a "version" field, LICENSE, or Don't depend on "fold_transition" from the path source, you are in example/.

pwd
# .../Fold_Transition          ← correct
# .../Fold_Transition/example  ← wrong

cd /path/to/Fold_Transition
ls LICENSE pubspec.yaml lib/fold_transition.dart
dart pub publish --dry-run

1. Accounts and access #

You need:

  1. Flutter SDK on PATH (flutter doctor clean enough to build Android). This package: Dart ^3.5.0, Flutter >=3.24.0.
  2. A Google Account (Gmail or any Google-backed email). pub.dev uses it for upload permission.
  3. The legal right to ship everything in the tarball (MIT code, shaders, screenshots).
  4. Write access to https://github.com/techlapse/fold_transition (the repository / issue_tracker URLs in pubspec.yaml).

Invite at least one other org member as a pub.dev uploader or verified-publisher admin so the package is not locked to a single person.

Do this once for the org (for example techlapse.com / techlapse.dev):

  1. Open pub.dev and sign in with Google.
  2. User menu → Create Publisher.
  3. Enter the domain you control.
  4. Complete Google Search Console verification (often a DNS TXT record). DNS can take hours.
  5. Invite the rest of the team as publisher members.

First version of a brand-new package cannot be published directly onto a publisher. Workaround from Dart:

  1. dart pub publish while signed in as a personal Google Account.
  2. Open https://pub.dev/packages/fold_transition/admin.
  3. Transfer to publisher. This cannot be reversed.

Later versions can be published by any publisher member (or by GitHub Actions once automated publishing is enabled).

3. What must exist in the repo #

pub.dev builds the package page from these files. Missing any of them costs pub points or fails validation.

File Role
pubspec.yaml Name, version, description (≤ 180 chars), SDK, repository, homepage, topics, screenshots, plugin platforms
README.md Package page body (this file)
CHANGELOG.md Scores tab + version history. Heading must match the version you publish (## 0.1.0)
LICENSE Required. This repo is MIT
lib/fold_transition.dart Public API barrel. Every public member needs dartdoc (public_member_api_docs)
example/ Runnable app. example/pubspec.yaml keeps publish_to: 'none' — only the plugin is published, the example is bundled as a demo
test/ Tests. Not required to upload, required for pub points
screenshots/blur_dissolve.png Listed in pubspec.yaml screenshots: — file must exist or publish fails
android/ FoldTransitionPlugin (com.techlapse.fold_transition)
shaders/bend.frag Bundled with the plugin

Do not add publish_to: 'none' on the root pubspec.yaml. That blocks pub.dev. Only the example app uses publish_to: 'none'.

Root pubspec.yaml fields that affect the listing:

name: fold_transition
version: 0.1.1
homepage: https://github.com/techlapse/fold_transition
repository: https://github.com/techlapse/fold_transition
issue_tracker: https://github.com/techlapse/fold_transition/issues
topics:
  - foldable
  - animation
  - hinge
  - android

Description must stay ≤ 180 characters. It is already at the limit.

Never depend on git / path / non-pub hosted packages in the root pubspec.yaml. pub.dev only allows hosted pub.dev deps and sdk: flutter.

4. GitHub repository #

If origin is empty:

  1. Create a public GitHub repo techlapse/fold_transition (private packages cannot be fetched by typical consumers).
  2. git remote add origin git@github.com:techlapse/fold_transition.git
  3. Push main.
  4. Confirm pubspec.yaml repository: matches the real URL.

.gitignore already excludes .dart_tool/, /build/, /pubspec.lock (correct for a library). If dart pub publish --dry-run warns about checked-in files that are also gitignored, either stop ignoring them or stop tracking them. Use .pubignore when you want different publish vs git rules: https://dart.dev/go/pubignore

5. Versioning #

Semantic versions:

Change Bump Example
Bug fix, docs PATCH 0.1.0 → 0.1.1
New API, compatible MINOR 0.1.1 → 0.2.0
Breaking API MAJOR 0.2.0 → 1.0.0 (or 0.3.0 while still 0.x)
Prerelease suffix 0.2.0-dev.1 — hidden from normal search, does not replace README

Every bump:

  1. version: in root pubspec.yaml
  2. Matching ## x.y.z section at the top of CHANGELOG.md
  3. Git tag after a successful publish: git tag v0.1.0 && git push origin v0.1.0

You cannot reuse a version number. Yanking (retract) does not free it.

6. Pre-publish gates #

Run from the package root (Fold_Transition/). Paste real output into the PR. Zero issues, including infos.

dart format --set-exit-if-changed .
dart analyze
flutter test
cd example && flutter test
cd example && flutter build apk --debug

Package-specific checks for this plugin:

grep -rn "\.resolution" lib/src/fold_mode_detector.dart   # must be empty
grep -rn "Future.delayed" test/                           # must be empty
grep -rn "SSENSOR\|folding_angle" lib/ android/ example/lib example/android example/test
grep -n "controller.value =" lib/src/fold_animator.dart   # only debugSetValue

Optional local pub-points preview (installs pana):

dart pub global activate pana
dart pub global run pana --no-warning .

Aim for 160/160. Typical deductions:

  • Missing / thin README, CHANGELOG, or LICENSE
  • Analysis warnings or infos
  • Public API without dartdoc
  • Missing example
  • Tight SDK constraints
  • Missing repository / homepage
  • Platforms not detected (this plugin declares Android only)

7. Dry run (always) #

dart pub publish --dry-run

Read:

  1. File list — cancel if you see secrets, key.properties, keystores, .env, local.properties, huge build/ trees, or personal screenshots.
  2. Warnings — dirty git, gitignore mismatches, missing docs.
  3. Archive size — keep gzip well under 100 MB (this package is ~0.5 MB).

Do not publish until git is clean (git status) so the uploaded tree matches the tag.

8. First publish (human) #

# From a clean main, version already bumped + CHANGELOG written
dart pub publish
  1. Browser opens → sign in with the Google Account that should own the package.
  2. Confirm the file list.
  3. Wait a few minutes → https://pub.dev/packages/fold_transition
  4. Transfer to the verified publisher (Admin tab) if you created one.
  5. Add extra uploaders on the Admin tab if you are not using a publisher.
  6. Tag the commit: git tag v0.1.0 && git push origin v0.1.0

Consumers then:

dependencies:
  fold_transition: ^0.1.1

Why search does not show the package yet #

The package page is live as soon as publish succeeds:

https://pub.dev/packages/fold_transition

Search is a separate index. It can take 15–30 minutes to pick up a new package. Until then, searching fold_transition returns unrelated hits (foldable_list, scaffolds, icons) and will not list this one. Google Search is slower (days); new packages are delayed on purpose as anti-spam.

This package is not unlisted and not discontinued. After indexing, it still ranks near the bottom of generic queries (fold, animation) because ranking uses likes, downloads, and pub points. Right after upload those are all zero until pana finishes.

There is no extra “enable search” switch. Do this:

  1. Open the direct URL above. If that 404s, publish did not succeed.
  2. Wait for the Scores tab to show real pub points (often 30–60 minutes).
  3. Search "fold_transition" or package:fold_transition on pub.dev.
  4. After topics index, try topic:foldable.
  5. Transfer to a verified publisher (Admin tab). That is the org trust badge; it does not instantly boost rank.
  6. Do not mark the package Unlisted or Discontinued.
  7. Do not leave a prerelease (0.1.0-dev.1) as the only version — those are hidden from normal search.

Ranking over the following weeks comes from pub points (aim 160), likes, and apps that depend on fold_transition: ^0.1.0. The word “fold” will still prefer older popular packages.

9. Later publishes #

  1. Land the change on main.
  2. Bump version + CHANGELOG.md.
  3. Gates in section 6 + dart pub publish --dry-run.
  4. dart pub publish (any publisher member / uploader).
  5. Push tag vX.Y.Z.

10. Automated publishing (after the first version) #

https://dart.dev/tools/pub/automated-publishing

On https://pub.dev/packages/fold_transition/admin enable Publishing from GitHub Actions. Restrict to this repo and a tag pattern such as v[0-9]+.[0-9]+.[0-9]+*.

Then add .github/workflows/publish.yml:

name: Publish to pub.dev

on:
  push:
    tags:
      - 'v[0-9]+.[0-9]+.[0-9]+*'

jobs:
  publish:
    permissions:
      id-token: write
    uses: dart-lang/setup-dart/.github/workflows/publish.yml@v1

Do not store a pub.dev token in GitHub Secrets. Trusted publishing uses OIDC (id-token: write).

Suggested CI on every PR (.github/workflows/ci.yml):

name: CI

on:
  push:
    branches: [main]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: subosito/flutter-action@v2
        with:
          channel: stable
      - run: dart format --set-exit-if-changed .
      - run: dart analyze
      - run: flutter test

11. Screenshot for the pub.dev listing #

pubspec.yaml already declares:

screenshots:
  - description: Blur-dissolve fold transition driven by a live hinge angle.
    path: screenshots/blur_dissolve.png

The PNG must exist at that path (keep it small; this is not the example APK). Capture from the example on a foldable or from Chrome + Trigger. Do not commit unrelated device photos.

12. Retract, restore, discontinue #

  • Retract (Admin tab): within 7 days of publish. Stops new apps from resolving that version. Existing pubspec.lock pins still work. Restore within 7 days of retraction.
  • Prefer a new patch over retract for ordinary bugs.
  • Discontinue: package stays up, badge shows, drops out of search. Optional replacement package name.

13. Release checklist #

Copy this into the release PR:

  • ❌ CHANGELOG.md has ## matching pubspec.yaml version
  • ❌ README install snippet uses that version
  • ❌ dart format --set-exit-if-changed .
  • ❌ dart analyze — no issues
  • ❌ flutter test and cd example && flutter test
  • ❌ Example still has the Activity configChanges + resizeableActivity
  • ❌ screenshots/blur_dissolve.png present
  • ❌ No SSENSOR / folding_angle in lib/, android/, example sources
  • ❌ dart pub publish --dry-run — expected files only, warnings understood
  • ❌ Git working tree clean
  • ❌ dart pub publish
  • ❌ Confirm https://pub.dev/packages/fold_transition/versions
  • ❌ Transfer to verified publisher (first release only)
  • ❌ git tag vX.Y.Z && git push origin vX.Y.Z

14. What a correct Samsung log looks like #

Inner display, one close:

fold_transition.boot: logging enabled
fold_transition.effect: prepare type=BlurDissolveEffect success=true
fold_transition.sensor: capability hasAngleSensor=true maxDegrees=180.0 ...
fold_transition.posture: from=unknown to=flat degrees=180.0
fold_transition.posture: from=flat to=halfOpen degrees=90.0
fold_transition.posture: from=halfOpen to=closed degrees=0.0
fold_transition.trigger: reason=posture mode=triggered duration=500ms
fold_transition.anim: status dismissed → forward
fold_transition.capture: taken width=… height=… attempt=1
fold_transition.anim: status forward → completed
fold_transition.capture: snapshot disposed

No mode promotion line — 0 / 90 / 180 are never intermediate.

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