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.
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.
58 Tech
Official tech channel of 58, a platform for tech innovation, sharing, and communication.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.