napa_widgets

Inspectable and serializable wrappers around standard Flutter widgets. Build widget trees at runtime, serialize them to JSON, deserialize them back, and inspect or modify properties on the fly — with optional Lua scripting for custom painting and gesture logic.

Features

  • Runtime inspection — every widget exposes its properties through a unified Inspectable interface, making it easy to build property editors and live preview tools
  • JSON serialization — full round-trip toJson() / decode() for the entire widget tree, including nested children and complex types
  • Widget registration — a global registry allows custom widgets to plug into the same decode pipeline
  • Lua scriptingNapaCustomPaint, NapaFlow, and NapaGestureDetector accept inline Lua scripts for runtime-defined drawing and interaction logic
  • 70+ widgets covered — layout, scrolling, animation, semantics, text, interaction, and more

Usage

Composing a widget tree

Build a tree of NapaWidgets and call toWidget() to get the standard Flutter widget:

import 'package:napa_widgets/napa_widgets.dart';

final napaTree = NapaPadding(
  padding: const EdgeInsets.all(16),
  child: NapaColumn(
    children: [
      NapaText('Hello, world!'),
      NapaSizedBox(height: 8),
      NapaContainer(
        color: const Color(0xFF2196F3),
        width: 120,
        height: 40,
      ),
    ],
  ),
);

// Convert to a real Flutter widget
Widget flutterWidget = napaTree.toWidget();

Serialization

Serialize a widget tree to JSON and restore it:

// Serialize
Map<String, dynamic> json = napaTree.toJson();

// Deserialize
NapaWidget? restored = NapaWidget.decode(json);

A serialized NapaText looks like:

{
  "_name": "Text",
  "data": "Hello, world!",
  "style": {
    "fontSize": 16.0,
    "color": { "r": 0.0, "g": 0.0, "b": 0.0, "a": 1.0, "colorSpace": "sRGB" }
  }
}

Runtime inspection

Every widget exposes its properties via the Inspectable mixin:

final widget = NapaContainer(color: Colors.red, width: 100, height: 80);

for (final prop in widget.properties) {
  print('${prop.name}: ${prop.getValue(widget)}');
}

// Modify a property at runtime
final colorProp = widget.properties.firstWhere((p) => p.name == 'color');
colorProp.setValue(widget, Colors.blue, null);

Registering custom widgets

Custom NapaWidget subclasses can be registered once at app startup:

NapaWidget.registerNapaWidget('MyWidget', MyNapaWidget.decode);

After registration, NapaWidget.decode(json) will dispatch to your class whenever _name == 'MyWidget'.

Lua scripting

NapaCustomPaint accepts an inline Lua script that implements a paint(canvas, size) function. The ui.* namespace exposes ~80 drawing primitives:

final painter = NapaCustomPaint(
  painter: NapaCustomPainter(
    type: NapaCustomPainterType.lua,
    script: '''
      function paint(canvas, size)
        local paint = ui.newPaint()
        ui.paintSetColor(paint, ui.newColor(0.2, 0.6, 1.0, 1.0))
        ui.canvasDrawRect(canvas,
          ui.newRect(0, 0, size.width, size.height), paint)
      end
    ''',
  ),
);

Implicit animations

All Flutter implicit animation widgets are supported. Changing a property and rebuilding produces a smooth transition:

NapaAnimatedContainer(
  duration: const Duration(milliseconds: 300),
  curve: Curves.easeInOut,
  width: isExpanded ? 200 : 100,
  color: isExpanded ? Colors.blue : Colors.grey,
  child: NapaText('Tap me'),
)

Rich text

NapaRichText uses the NapaTextSpan data class for fully serializable styled text trees:

NapaRichText(
  text: NapaTextSpan(
    children: [
      NapaTextSpan(text: 'Hello ', style: NapaTextStyle(fontSize: 16)),
      NapaTextSpan(
        text: 'world',
        style: NapaTextStyle(fontSize: 16, color: const Color(0xFF2196F3)),
      ),
    ],
  ),
)

Widget coverage

Widget Status Lua scripting Notes
AbsorbPointer
Align
AnimatedAlign
AnimatedContainer
AnimatedCrossFade
AnimatedOpacity
AnimatedPadding
AnimatedPositioned Must be child of Stack
AnimatedRotation
AnimatedScale
AnimatedSize
AnimatedSlide
AnimatedSwitcher
AspectRatio
BackdropFilter
Baseline
Center
ClipOval No CustomClipper support
ClipPath No CustomClipper support
ClipRect No CustomClipper support
ClipRRect No CustomClipper support
ClipRSuperellipse No CustomClipper support
ColoredBox
Column
ConstrainedBox
Container
CustomPaint
DecoratedBox
DefaultTextStyle
Directionality
ExcludeSemantics
Expanded
FittedBox
Flex
Flexible
Flow
Focus No event callbacks
FocusScope No event callbacks
FractionallySizedBox
GestureDetector
GridView GridView.count variant
Icon
IgnorePointer
Image
ImageFiltered
IndexedStack
InteractiveViewer
IntrinsicHeight
IntrinsicWidth
LimitedBox
Listener
ListView
MergeSemantics
MouseRegion
Offstage
Opacity
OverflowBox
Padding
PageView
PhysicalModel
Positioned
RepaintBoundary
RichText
RotatedBox
Row
SafeArea
Semantics
SingleChildScrollView
SizedBox
SizedOverflowBox
Stack
Text
Transform
UnconstrainedBox
Visibility
Wrap

✅ Implemented    ⏳ Pending

Architecture

All widgets extend NapaWidget, which mixes in Inspectable from the inspectable_property package:

NapaWidget (abstract, Inspectable mixin)
├── NapaStatelessWidget
├── NapaStatefulWidget
├── NapaRenderObjectWidget
│   ├── NapaSingleChildRenderObjectWidget
│   └── NapaMultiChildRenderObjectWidget
└── NapaProxyWidget
    └── NapaParentDataWidget

Each widget implements three core capabilities:

  • toWidget() — renders to a real Flutter Widget
  • toJson() — serializes to Map<String, dynamic> (includes _name field)
  • static decode(data) — deserializes from JSON

Libraries

napa_widgets
scripting/lua/lua_ui_lib
scripting/napa_script_type
widgets/data/image_wrapper
widgets/data/napa_color_filter
widgets/data/napa_image_filter
widgets/data/napa_mask_filter
widgets/data/napa_matrix4
widgets/data/napa_paint
widgets/data/napa_shader
widgets/data/napa_text_decoration
widgets/data/napa_text_scaler
widgets/data/napa_text_span
widgets/data/napa_text_style
widgets/napa_absorb_pointer
widgets/napa_align
widgets/napa_animated_align
widgets/napa_animated_container
widgets/napa_animated_cross_fade
widgets/napa_animated_opacity
widgets/napa_animated_padding
widgets/napa_animated_positioned
widgets/napa_animated_rotation
widgets/napa_animated_scale
widgets/napa_animated_size
widgets/napa_animated_slide
widgets/napa_animated_switcher
widgets/napa_aspect_ratio
widgets/napa_backdrop_filter
widgets/napa_baseline
widgets/napa_box_scroll_view
widgets/napa_center
widgets/napa_clip_oval
widgets/napa_clip_path
widgets/napa_clip_rect
widgets/napa_clip_rrect
widgets/napa_clip_rsuperellipse
widgets/napa_colored_box
widgets/napa_column
widgets/napa_constrained_box
widgets/napa_container
widgets/napa_custom_paint
widgets/napa_decorated_box
widgets/napa_default_text_style
widgets/napa_directionality
widgets/napa_exclude_semantics
widgets/napa_expanded
widgets/napa_fitted_box
widgets/napa_flex
widgets/napa_flexible
widgets/napa_flow
widgets/napa_focus
widgets/napa_focus_scope
widgets/napa_fractionally_sized_box
widgets/napa_gesture_detector
widgets/napa_grid_view
widgets/napa_icon
widgets/napa_ignore_pointer
widgets/napa_image
widgets/napa_image_filtered
widgets/napa_indexed_stack
widgets/napa_interactive_viewer
widgets/napa_intrinsic_height
widgets/napa_intrinsic_width
widgets/napa_limited_box
widgets/napa_list_view
widgets/napa_listener
widgets/napa_merge_semantics
widgets/napa_mouse_region
widgets/napa_multi_child_render_object_widget
widgets/napa_offstage
widgets/napa_opacity
widgets/napa_overflow_box
widgets/napa_padding
widgets/napa_page_view
widgets/napa_parent_data_widget
widgets/napa_physical_model
widgets/napa_positioned
widgets/napa_proxy_widget
widgets/napa_render_object_widget
widgets/napa_repaint_boundary
widgets/napa_rich_text
widgets/napa_rotated_box
widgets/napa_row
widgets/napa_safe_area
widgets/napa_scroll_view
widgets/napa_semantics
widgets/napa_single_child_render_object_widget
widgets/napa_single_child_scroll_view
widgets/napa_sized_box
widgets/napa_sized_overflow_box
widgets/napa_stack
widgets/napa_stateful_widget
widgets/napa_stateless_widget
widgets/napa_text
widgets/napa_transform
widgets/napa_unconstrained_box
widgets/napa_visibility
widgets/napa_widget
widgets/napa_wrap
widgets/tojson_extensions