Mobile Development 13 min read

Fair 2.0 Practice in Anjuke Photo App: Architecture, Dynamic Capabilities, and Performance Evaluation

This article introduces Fair 2.0, a Flutter dynamic framework used in the Anjuke Photo App to enable runtime widget updates, explains how to embed dynamic pages, register local widgets and templates, and presents performance data such as package size, memory usage, startup time, and frame rate.

58 Tech
58 Tech
58 Tech
Fair 2.0 Practice in Anjuke Photo App: Architecture, Dynamic Capabilities, and Performance Evaluation

Fair 2.0 is a dynamic framework for Flutter that enables runtime widget updates by converting Dart source files with the Fair Compiler, providing projects with the ability to dynamically update UI without a full re‑release.

The project (GitHub: https://github.com/wuba/fair ) is applied in the Anjuke Photo App (安居拍房App) to address urgent, uncertain requirements by allowing dynamic code delivery.

The existing architecture follows a three‑tier hybrid model where Flutter artifacts are packaged as AAR or Framework and integrated into native Android and iOS projects.

Dynamic UI Registration

Unified dynamic page registration is added to MaterialApp.routes :

FairApp(
      child: MaterialApp(
        home: ***,
        routes: {
          // Fair dynamic page navigation
          'fair_page': (context) => FairWidget(
              name: _getParams(context, 'name'),
              path: _getParams(context, 'path'),
              data: {'fairProps': jsonEncode(_getData(context, _getParams(context, 'name')))}),
        },
      ),
    )

Navigation to a dynamic page uses Navigator.pushNamed with arguments that specify the page name, JSON bundle path, and initial data:

Navigator.pushNamed(context, 'fair_page', arguments: {
  'name': 'Dynamic Page **',
  'path': 'assets/bundle/lib_src_page_logic-page_sample_logic_page.fair.json',
  'data': {"fairProps": {"pageName": 'Dynamic Page **', "_count": 58}}
});

Partial Element Dynamicization

Dynamic items can be inserted into native lists by checking the item type and rendering a FairWidget when appropriate:

Widget getItem(var item) {
  if (item.type == 'fair') {
    return Container(
        alignment: Alignment.centerLeft,
        color: Colors.white,
        constraints: BoxConstraints(minHeight: 80),
        child: FairWidget(
          name: item.id,
          path: dynamicResourceName,
          data: {/** parameters **}));
  } else {
    return Column(/* native content */);
  }
}

Local Widget Conversion

Local widgets can be exposed to dynamic pages using the @FairBinding annotation:

@FairBinding()
class CardWidget extends StatelessWidget {
  String text;
  CardWidget({this.text});
  @override
  Widget build(BuildContext context) {
    return Text(text, style: TextStyle(color: Colors.red));
  }
}

After running flutter pub run build_runner build , the generated module is registered in FairApp :

FairApp(child: MyApp(), generated: AppGeneratedModule());

In a dynamic page, the bound widget can be used normally:

@FairPatch()
class CardWidgetState extends State
{
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.yellow,
      child: Column(children: [
        Row(children: [CardWidget(text: 'card 1')]),
      ]),
    );
  }
}

Logic Template Registration

To reuse common logic, a delegate extending FairDelegate can register functions such as item builders and refresh handlers:

class ListDelegate extends FairDelegate {
  @override
  Map
bindFunction() {
    var functions = super.bindFunction();
    functions.addAll({
      '_itemBuilder': _itemBuilder,
      '_onRefresh': _onRefresh,
    });
    return functions;
  }

  Future
_onRefresh() async {
    await runtime?.invokeMethod(pageName, '_onRefresh', null);
  }

  Widget _itemBuilder(context, index) {
    var result = runtime?.invokeMethodSync(pageName, '_onItemByIndex', [index]);
    return FairWidget(name: itemData, path: '***', data: {'**'});
  }
}

FairApp(
  delegate: {'ListLoadMore': (ctx, _) => ListDelegate()},
  child: MaterialApp(home: ***),
);

Third‑Party Plugin Extension

Plugins can be exposed to dynamic code by implementing IFairPlugin and registering methods:

class WBPermission extends IFairPlugin {
  Future
requestPermission(map) async {
    isGranted = await Permission.photos.request().isGranted;
    return Future.value();
  }

  @override
  Map
getRegisterMethods() {
    var functions =
{};
    functions.putIfAbsent('requestPermission', () => requestPermission);
    return functions;
  }
}

Integrated Architecture After Adding Fair

The architecture now includes pre‑embedded capabilities (network, permission, storage, image picker) and registers them so that dynamic modules can call them seamlessly.

Performance Data

Package size increase: +13.2 MB on Android (dual‑SO), +5.6 MB on iOS (arm64+armv7).

Memory increase: +20 MB on Android, +17.9 MB on iOS.

Startup time increase: +0.05 s on Android, +0.1 s on iOS.

Frame rate impact: negligible on both platforms.

Test environment: Honor V40 (Android 10, 8 GB RAM) and iPhone XS Max (iOS 13.3, 4 GB RAM) using Flutter engine 1.17.3.

Conclusion

Integrating Fair gave the Anjuke Photo App dynamic update capabilities, allowing rapid response to urgent, unclear requirements while keeping performance overhead minimal; developers are encouraged to pre‑embed common capabilities (network, permission, storage, image selection) to maximize the benefits of widget‑level dynamicization.

FlutterMobile DevelopmentPerformancearchitecturedynamic UIFair 2.0
58 Tech
Written by

58 Tech

Official tech channel of 58, a platform for tech innovation, sharing, and communication.

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.