跪拜 Guibai
← Back to the summary

Patchwork: A Dart/Flutter Tool That Patches Third-Party Packages Without Forking

Flutter Patchwork: A Third-Party Tool for Modifying Dependency Package Source Code Without Forking

Actually, this is quite common in the JS ecosystem, similar to Node.js's patch-package. Patchwork is a dependency package patch management tool specifically designed for Dart/Flutter projects.

The functionality is actually very simple:

When you need to modify the source code of a third-party pub package (e.g., to fix a bug, temporarily add a feature), instead of forking the entire repository, you can simply use Patchwork to generate a .patch file and save it in your project.

That is, when you encounter a problem, you can directly fix it locally first, get it running, then sync it with your team, and finally, when you have time, turn the patch into a PR and submit it to the package maintainer.

Command Purpose
dart run patchwork patch <package-name> Start an editable patch session, copy package source locally
dart run patchwork patch --commit <package-name> Commit your edits as a .patch file
dart run patchwork apply Apply all committed patches to the project
dart run patchwork status View patch and override status
dart run patchwork doctor Check if the local environment is ready (e.g., whether git is installed)

So this patch file can be committed to git. Everyone can run patchwork apply to overlay and use it, while keeping pubspec.yaml unpolluted and the original third-party local dependencies unpolluted. You can also use the patch as a temporary fix before the official release.

Currently, the entire workflow is mainly divided into three stages. The first is creating a session via patchwork patch <package-name>:

dart run patchwork patch greeter

This command mainly does:

Then sessions/pub/[email protected] writes session metadata (recording paths, versions, etc.):

.dart_tool/patchwork/
  baseline/pub/[email protected]/   // original snapshot
  edit/pub/[email protected]/        // user edits here
  sessions/pub/[email protected]

Then comes the second stage: patchwork patch --commit <package-name> generates the patch file. It essentially generates diff information by calling the system git diff --no-index on the baseline/ and edit/ directories, producing standard unified diff format text:

final arguments = [
  'diff', '--no-ext-diff', '--no-color',
  '--src-prefix=a/', '--dst-prefix=b/',
  '--no-index',
  baselinePath,
  editPath,
];

Then comes patch verification: in a temporary directory, copy the baseline again and use git apply --check to verify whether the generated patch can work correctly.

Finally, the third stage is patchwork apply. The general process is:

# Auto-generated, do not modify manually
dependency_overrides:
  greeter:
    path: .dart_tool/patchwork/store/pub/[email protected]_patch_hash=xxxx

After that, you just need to run pub get, and pub will use the patched local copy via the path override. So the entire implementation is not particularly complex, but the idea is quite good.

At least it does not modify pubspec.yaml, only intrudes on pubspec_overrides.yaml. Only two things are committed to git: patchwork.lock and patches/pub/*.patch. Everything under .dart_tool/patchwork/ is reproducible generated modules. On failure, it can also automatically roll back (the commit stage has a file snapshot mechanism).

It's quite suitable for scenarios involving temporary fixes that require team collaboration and rapid packaging. After all, in the past, there really were people who directly modified the local package, fixed the bug, and then committed it — that kind of "genius". This Patchwork is actually somewhat useful.

Links

https://github.com/medz/patchwork