dartnative_assists
Option+Enter widget assists and DartNative-specific warnings for DartNative projects. Runs inside the Dart Analysis Server, so it works the same in Android Studio, IntelliJ and VS Code.
Why
The analysis server ships "Wrap with Container", "Convert to StatefulWidget"
and the rest of the Flutter assists, but each one checks that the widget
class comes from package:flutter/src/widgets/framework.dart. DartNative's
Widget lives in package:dartnative, so in a DartNative file those assists
never appear. This plugin re-implements them against DartNative's types.
It also turns the DartNative behaviours that fail silently or crash at mount into analyzer warnings, so they are caught while typing instead of on a device.
Setup
In the analysis_options.yaml at the root of your project:
plugins:
dartnative_assists: ^0.1.1
Restart the Dart Analysis Server once (Android Studio: Tools › Dart › Restart Dart Analysis Server; VS Code: "Dart: Restart Analysis Server"). The first restart after enabling builds the plugin isolate and takes a few seconds.
Requires Dart 3.11 or later; the DartNative dn toolchain ships a newer SDK.
Assists
With the cursor on a widget creation (the type name or anywhere in its arguments):
| Assist | Result |
|---|---|
| Wrap with Center / Container / SizedBox / Expanded / Flexible / SafeArea / GestureDetector / GlassEffectContainer | Name(child: …) |
| Wrap with Padding | Padding(padding: const EdgeInsets.all(8.0), child: …) |
| Wrap with Column / Row / Stack | multi-line children: [ … ] |
| Wrap with Builder | Builder(builder: (context) => …) |
| Wrap with FutureBuilder / StreamBuilder / ValueListenableBuilder | …builder: (context, snapshot) => … |
| Wrap with widget… | a wrapper name you type |
| Remove this widget | replaces a wrapper with its single child |
| Swap with child / Swap with parent | exchanges two single-child: widgets, keeping their other arguments |
| Convert to children: / Convert to child: | switches between the two slots |
| Move widget up / down | reorders inside a children: list |
With the cursor on a class header line:
| Assist | Result |
|---|---|
| Convert to StatefulWidget | constructors and final fields stay on the widget; other members move to _XState; field references become widget.x, statics become X.y, $x interpolations become ${widget.x} |
| Convert to StatelessWidget | merges the State back; offered only when the State has no lifecycle overrides, mixins, or setState calls |
Warnings (on by default)
| Rule | What it catches |
|---|---|
dartnative_fab_slot_android_only |
Scaffold.floatingActionButton, which renders on Android and shows nothing on iOS |
dartnative_menu_action_must_be_alone |
a BarButtonItem with menu: beside other actions, which asserts at mount |
dartnative_uniform_border_only |
per-side Border(...), which DartNative ignores in favour of top |
dartnative_mirror_text_controller |
a TextField with a controller and no onChanged; the controller is one-way. Quick fix adds the mirror |
dartnative_custom_paint_finite_size |
CustomPaint(size: Size(double.infinity, …)), which paints off-screen |
dartnative_positioned_must_be_outermost |
a wrapper above Positioned in a Stack, which is dropped |
dartnative_snackbar_action_not_wired |
SnackBarAction.onPressed, which never fires |
Opt-in lint:
| Rule | What it catches |
|---|---|
dartnative_offstage_loses_state |
Offstage, which unmounts its child in DartNative |
Enable it under the plugin entry:
plugins:
dartnative_assists:
version: ^0.1.1
diagnostics:
dartnative_offstage_loses_state: true
Suppress any rule on a line with // ignore: dartnative_assists/<rule>.
Development
dart pub get
dart test # resolves against a stub package:dartnative, no SDK needed
dart analyze
tool/try_assists.dart runs the assists against a real file inside a
DartNative project:
dart run tool/try_assists.dart path/to/app/lib/main.dart --find "Text(" --apply 1
The analysis_server_plugin and analyzer dependencies move in lockstep
with the Dart SDK. After a dn upgrade, run dart pub upgrade and the tests.
When the plugin is enabled from a local path: and you change its source,
the analysis server keeps running the isolate it already compiled. Delete
~/.dartServer/.plugin_manager and restart the server to pick up the change.
An "error occurred while executing an analyzer plugin" line during
dn analyze after an edit is that stale isolate, not a bug in the change.
Sources of the rules
Each warning encodes one fact from the DartNative widget reference or from
on-device testing on DartNative 1.0.0 (September 2026). The rule's
description names which. If DartNative changes a behaviour, the rule should
be retired rather than kept as folklore.
Libraries
- dartnative_assists
- Public entry point of the package, for tooling that expects a library
named after the package. The analysis server itself loads
main.dart, which is the file name the plugin system requires. - main
- dartnative_assists: analysis-server plugin entry point.