Mobile Development 13 min read

A New Approach to State Refresh in Flutter Using Extension Properties and TosObWidget

This article introduces a novel Flutter state‑refresh technique that leverages Dart extension properties to implement an observer pattern, compares it with the traditional Provider/ChangeNotifier approach, and demonstrates a more granular, flexible solution called TosObWidget with complete code examples and performance analysis.

JD Tech
JD Tech
JD Tech
A New Approach to State Refresh in Flutter Using Extension Properties and TosObWidget

The article presents a new idea for refreshing UI state in Flutter applications by defining an observer pattern through Dart extension properties, allowing widgets to subscribe to data changes and update automatically.

Background : Declarative UI frameworks such as Flutter, React Native, SwiftUI, and Compose have made state management a core concern. Traditional imperative UI required manual set calls, while declarative UI automatically rebuilds when observed data changes.

1.1 Declarative UI

Declarative UI is not new, but its popularity has surged in recent years across mobile and web platforms.

1.2 State in Declarative UI

State represents data that widgets depend on; when the data changes, the UI refreshes, decoupling logic from presentation.

2. Provider State Management

The article first shows a typical Provider‑based implementation using ChangeNotifierProvider , Consumer , and Selector . The code creates a SecondPageModel that extends ChangeNotifier and updates two text fields when a button is pressed.

class SecondPage extends StatelessWidget {
  final _model = SecondPageModel();
  @override
  Widget build(BuildContext context) => ChangeNotifierProvider(
        create: (_) => _model,
        child: Scaffold(
          body: Container(
            alignment: Alignment.center,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Consumer
(builder: (context, model, child) => Text(model.textA)),
                Selector
(
                  builder: (context, model, child) => Text(model),
                  selector: (context, secondPageModel) => secondPageModel.textB,
                ),
                Consumer
(
                  builder: (context, model, child) => TextButton(
                    onPressed: () => model.textA = "你好",
                    child: Text("按钮"),
                  ),
                ),
              ],
            ),
          ),
        ),
      );
}

class SecondPageModel with ChangeNotifier {
  String _textA = "hello";
  String _textB = "world";
  String get textA => _textA;
  set textA(String value) { _textA = value; notifyListeners(); }
  String get textB => _textB;
  set textB(String value) { _textB = value; notifyListeners(); }
}

The author notes that Provider requires a top‑level provider, can be inflexible, and may refresh more widgets than necessary.

3. New State‑Management Practice with TosObWidget

Instead of Provider, the article introduces TosObWidget , a lightweight widget that observes RxObj instances created via a .tos extension on any value. Only the widgets that depend on changed data are rebuilt.

class FirstPage extends StatelessWidget {
  final _model = FirstPageModel();
  @override
  Widget build(BuildContext context) => Scaffold(
        body: Container(
          alignment: Alignment.center,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              TosObWidget(() => Text(_model.textA.value)),
              TosObWidget(() => Text(_model.textB.value)),
              TextButton(
                onPressed: () => _model.textA.value = "你好",
                child: Text("按钮"),
              ),
            ],
          ),
        ),
      );
}

class FirstPageModel {
  final textA = "hello".tos;
  final textB = "world".tos;
}

The TosObWidget implementation consists of an abstract _ObzWidget and its state _ObzState , which holds an RxObserver . During build , the widget temporarily registers its observer in a static proxy, allowing any RxObj accessed in the build closure to attach to that observer.

typedef WidgetCallback = Widget Function();

class TosObWidget extends _ObzWidget {
  final WidgetCallback builder;
  const TosObWidget(this.builder, {Key key}) : super(key: key);
  @override
  Widget build() => builder();
}

abstract class _ObzWidget extends StatefulWidget {
  const _ObzWidget({Key key}) : super(key: key);
  @protected
  Widget build();
}

class _ObzState extends State<_ObzWidget> {
  RxObserver _observer = RxObserver();
  @override
  void initState() {
    _observer.observe(_updateUI);
    super.initState();
  }
  void _updateUI() { if (mounted) setState(() {}); }
  @override
  void dispose() { _observer?.close(); super.dispose(); }
  Widget get buildWidgets {
    final previous = RxObserver.proxy;
    RxObserver.proxy = _observer;
    final widgets = widget.build();
    RxObserver.proxy = previous;
    return widgets;
  }
  @override
  Widget build(BuildContext context) => buildWidgets;
}

The RxObj class stores a value, a list of observers, and notifies them when the value changes. The .tos extension creates an RxObj from any literal.

class RxObj
{
  T _value;
  bool _firstRebuild = true;
  final List
_observers = [];
  RxObj(this._value);
  RxObj.obj();
  T get value {
    if (RxObserver.proxy != null && !_observers.contains(RxObserver.proxy)) {
      _observers.add(RxObserver.proxy);
    }
    return _value;
  }
  set value(T val) {
    if (_value == val && _firstRebuild) return;
    _firstRebuild = false;
    _value = val;
    refresh();
  }
  void refresh() {
    for (var observer in _observers) {
      if (observer.canUpdate) observer.update();
    }
  }
}

extension RxT
on T {
  RxObj
get tos => RxObj
(this);
}

The RxObserver holds a callback and a static proxy used during widget building to link observers to RxObj instances.

class RxObserver
{
  VoidCallback update;
  bool get canUpdate => update != null;
  void close() { update = null; }
  static RxObserver proxy;
  void observe(VoidCallback upd) { update = upd; }
}

Comparison between Provider and TosObWidget shows fewer lines of code (37 vs 60), fewer required classes, and a smaller refresh scope (3 widgets vs 6), indicating better granularity and flexibility.

Conclusion : Using Dart extension properties and a custom observer widget (TosObWidget) provides a lightweight, fine‑grained state‑management solution for Flutter, reducing boilerplate and improving performance compared with the conventional Provider approach.

DartFlutterMobile DevelopmentState ManagementProviderTosObWidget
JD Tech
Written by

JD Tech

Official JD technology sharing platform. All the cutting‑edge JD tech, innovative insights, and open‑source solutions you’re looking for, all in one place.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.