Flutter Json Widgets
This goal of this project is to provide a way to create Flutter widgets with JSON.
This includes reading and writing to JSON from classes that represent widgets or other helper classes and enums.
This does not depend on the flutter sdk for the core classes and can be used in places like the server, command line and dart2js.
Implementation
This package relies on another package called freezed to generate the classes, enums and especially the unions that make it very helpful for parsing.
While it is possible to match the Flutter SDK api for widget creation in most cases, there are times where this will differ.
Named Constructors
Since the widget class is a sealed union things like ElevatedButton.icon
will be ElevatedButtonIcon
instead.
The class will still map to the correct widget at runtime.
Functions
Since this is JSON ultimatly, then logic will not work here. However, there are multiple types of intents that can be used with a class Callback
and various actions (navigation, messages, empty, ...).
Also planning on expanding it to include form submission and http requests too.
Builder Methods
Since there is no logic things like LayoutBuilder
are difficult to achieve. The MaterialApp
has a routes property that you can provide a static map to without depending on context.
Supported Widgets
There are a lot, and more coming soon. List of widgets here.
SSR Example
Server (dart)
// ignore_for_file: depend_on_referenced_packages
import 'dart:convert';
import 'dart:async';
import 'package:flutter_json_widgets/flutter_json_widgets.dart';
import 'package:shelf_router/shelf_router.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_cors_headers/shelf_cors_headers.dart';
int _counter = 0;
Future main() async {
final app = Router();
const host = 'localhost';
const port = 8080;
const url = 'http://$host:$port';
app.post('/api/counter', (Request request) async {
final content = await request.readAsString();
final map = jsonDecode(content) as Map<String, Object?>;
_counter = map['counter'] as int;
return Response.ok(
jsonEncode({'counter': _counter}),
headers: {
'Content-Type': 'application/json',
},
);
});
app.get(
'/',
(Request request) => _ui(const MaterialApp(
debugShowCheckedModeBanner: false,
initialRoute: '/counter',
routes: {
'/counter': NetworkWidget(
request: NetworkHttpRequest(
url: '$url/counter',
),
),
},
)),
);
app.get(
'/counter',
(Request request) => _ui(Scaffold(
appBar: const AppBar(
title: Text('Flutter Demo Home Page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: const TextStyle.headlineMedium(),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: Callback.networkRequest(
NetworkHttpRequest(
url: '$url/api/counter',
method: 'POST',
bodyMap: {'counter': _counter + 1},
),
callback: const Callback.reload(),
),
tooltip: 'Increment',
child: const Icon(Icons.add),
),
)),
);
// Set CORS headers with every request
final handler = const Pipeline().addMiddleware(corsHeaders()).addHandler(app);
// ignore: avoid_print
print('Starting server on $url');
await io.serve(handler, host, port);
}
Response _ui(Widget widget) {
const encoder = JsonEncoder.withIndent(' ');
final jsonString = encoder.convert(widget.toJson());
return Response.ok(
jsonString,
headers: {
'Content-Type': 'application/json',
},
);
}
Client (flutter)
import 'package:flutter/material.dart';
import 'package:flutter_json_widgets/flutter.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return FlutterWidget.network(
url: Uri.parse('http://localhost:8080/'),
);
}
}
JSON
Here is an example for the dart API:
import 'package:flutter_json_widgets/material.dart';
class Example {
int _counter = 0;
Widget build() {
return Scaffold(
appBar: const AppBar(
title: Text('Flutter Demo Home Page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: const TextStyle.headlineMedium(),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: Callback.networkRequest(
NetworkHttpRequest(
url: '$url/api/counter',
method: 'POST',
bodyMap: {'counter': _counter + 1},
),
callback: const Callback.reload(),
),
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
And the backing JSON:
{
"appBar": {
"automaticallyImplyLeading": true,
"title": {
"data": "Flutter Demo Home Page",
"runtimeType": "text"
},
"primary": true,
"excludeHeaderSemantics": false,
"toolbarOpacity": 1.0,
"bottomOpacity": 1.0,
"forceMaterialTransparency": false,
"runtimeType": "appBar"
},
"body": {
"child": {
"mainAxisAlignment": "center",
"mainAxisSize": "max",
"crossAxisAlignment": "center",
"verticalDirection": "down",
"children": [
{
"data": "You have pushed the button this many times:",
"runtimeType": "text"
},
{
"data": "0",
"style": {
"runtimeType": "headlineMedium"
},
"runtimeType": "text"
}
],
"runtimeType": "column"
},
"runtimeType": "center"
},
"floatingActionButton": {
"child": {
"icon": {
"codePoint": 57415,
"fontFamily": "MaterialIcons",
"matchTextDirection": false
},
"runtimeType": "icon"
},
"tooltip": "Increment",
"onPressed": {
"request": {
"url": "http://localhost:8080/api/counter",
"headers": {},
"method": "POST",
"bodyMap": {
"counter": 1
}
},
"callback": {
"runtimeType": "reload"
},
"runtimeType": "networkRequest"
},
"mini": false,
"clipBehavior": "none",
"autofocus": false,
"isExtended": false,
"runtimeType": "floatingActionButton"
},
"persistentFooterAlignment": {
"x": 1.0,
"y": 0.0
},
"primary": true,
"extendBody": false,
"extendBodyBehindAppBar": false,
"drawerEnableOpenDragGesture": true,
"endDrawerEnableOpenDragGesture": true,
"runtimeType": "scaffold"
}
Troubleshooting
For web you need to pass the flag --no-tree-shake-icons
to keep the icons.
Related projects
Libraries
- alignment
- Based on the Alignment class in the Flutter SDK.
- border_radius
- Based on the BorderRadius class in the Flutter SDK.
- border_side
- Based on the BorderSide class in the Flutter SDK.
- box_constraints
- Based on the BoxConstraints class in the Flutter SDK.
- callback
- Since JSON does not support functions, we need to use a custom class to map intents to callbacks.
- color
- Based on the Color class in the Flutter SDK.
- color_filter
- Based on the ColorFilter class in the Flutter SDK.
- color_scheme
- Based on the ColorScheme class in the Flutter SDK.
- colors
- Based on the Colors class in the Flutter SDK.
- curves
- Based on the Curve class in the Flutter SDK.
- data_cell
- Based on the DataCell class in the Flutter SDK.
- data_column
- Based on the DataColumn class in the Flutter SDK.
- data_row
- Based on the DataRow class in the Flutter SDK.
- decoration
- Based on the Decoration class in the Flutter SDK.
- decoration_image
- Based on the DecorationImage class in the Flutter SDK.
- Based on the DropdownMenuItem class in the Flutter SDK.
- edge_insets
- Based on the EdgeInsets class in the Flutter SDK.
- enums
- Based on the FloatingActionButtonLocation class in the Flutter SDK.
- floating_label_alignment
- Based on the FloatingLabelAlignment class in the Flutter SDK.
- flutter
- Flutter compatible import.
- flutter_json_widgets
- flutter_widget
- A transformer that converts a JSON string into a Flutter widget.
- font_feature
- Based on the FontFeature class in the Flutter SDK.
- font_variation
- Based on the FontVariation class in the Flutter SDK.
- font_weight
- Based on the FontWeight class in the Flutter SDK.
- form_data
- Since this is JSON we need to store an abstract type for form data.
- gradient
- Based on the Gradient class in the Flutter SDK.
- gradient_transform
- Based on the GradientTransform class in the Flutter SDK.
- icon_data
- Based on the IconData class in the Flutter SDK.
- icon_theme_data
- Based on the IconThemeData class in the Flutter SDK.
- icons
- This file is auto generated. Do not edit.
See
bin/icons.dart
for more information. /// Based on the Icons class in the Flutter SDK. - image_provider
- Based on the ImageProvider class in the Flutter SDK.
- inline_span
- Based on the InlineSpan class in the Flutter SDK.
- input_decoration
- Based on the InputDecoration class in the Flutter SDK.
- key
- Based on the Key class in the Flutter SDK.
- locale
- Based on the Locale class in the Flutter SDK.
- Based on the MaterialBanner class in the Flutter SDK.
- material_state_property
- Based on the MaterialStateProperty class in the Flutter SDK.
- matrix_4
- Based on the Matrix4 class in the Flutter SDK.
- mouse_cursor
- Based on the MouseCursor class in the Flutter SDK.
- Based on the NavigationRailDestination class in the Flutter SDK.
- network_request
- Since this is JSON we need to describe the network requests that will be made.
- offset
- Based on the Offset class in the Flutter SDK.
- paint
- Based on the Paint class in the Flutter SDK.
- Based on the PopupMenuEntry class in the Flutter SDK.
- preferred_size_widget
- Based on the PreferredSizeWidget class in the Flutter SDK.
- radius
- Based on the Radius class in the Flutter SDK.
- rect
- Based on the Rect class in the Flutter SDK.
- scroll_physics
- Based on the ScrollPhysics class in the Flutter SDK.
- shadow
- Based on the Shadow class in the Flutter SDK.
- shape_border
- Based on the ShapeBorder class in the Flutter SDK.
- size
- Based on the Size class in the Flutter SDK.
- sliver
- Class for creating Sliver widgets. The Flutter SDK uses the Widget class but can lead to improper usage.
- sliver_child_delegate
- Based on the SliverChildDelegate class in the Flutter SDK.
- sliver_grid_delegate
- Based on the SliverGridDelegate class in the Flutter SDK.
- snack_bar
- Based on the SnackBar class in the Flutter SDK.
- snack_bar_action
- Based on the SnackBarAction class in the Flutter SDK.
- strut_style
- Based on the StrutStyle class in the Flutter SDK.
- table_border
- Based on the TableBorder class in the Flutter SDK.
- table_column_width
- Based on the TableColumnWidth class in the Flutter SDK.
- table_row
- Based on the TableRow class in the Flutter SDK.
- text_align_vertical
- Based on the TextAlignVertical class in the Flutter SDK.
- text_decoration
- Based on the TextDecoration class in the Flutter SDK.
- text_height_behavior
- Based on the TextHeightBehavior class in the Flutter SDK.
- text_input_formatter
- Based on the TextInputFormatter class in the Flutter SDK.
- text_input_type
- Based on the TextInputType class in the Flutter SDK.
- text_style
- Based on the TextStyle class in the Flutter SDK.
- text_theme
- Based on the TextTheme class in the Flutter SDK.
- theme_data
- Based on the ThemeData class in the Flutter SDK.
- visual_density
- Based on the VisualDensity class in the Flutter SDK.
- widget
- Based on the Widget library in the Flutter SDK.