Duck
A Flutter package for creating dynamically updatable UI components with Over-The-Air (OTA) updates. Update your app's UI without going through app store approval.
Features
- Create UI components/elements that can be dynamically updated on production
- Update views without app store approval (OTA updates)
- Use public components from duck.flexxxlab.com
- Create custom components and integrate them into your app
- Simple integration with minimal setup
Installation
Add this to your package's pubspec.yaml file:
dependencies:
duck: ^0.0.1
Usage
Using Public Components
First, initialize Duck in your main.dart:
void main() {
// final environment = const String.fromEnvironment('TARGET_PLATFORM');
final environment = 'localhost';
String baseUrl;
switch (environment) {
case 'androidEmulator':
baseUrl = 'http://10.0.2.2:8000';
case 'localhost': // iOS simulator or web
baseUrl = 'http://localhost:8000';
default: // public cloud URL
baseUrl = 'https://duck.flexxxlab.com/api';
}
Duck(baseUrl: baseUrl, components: {});
runApp(const MyApp());
}
Then use the DuckCanvasWidget in your app:
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
final publicFirstCanvasId = '6956a659beea0c7c40b51e8c';
final canvasId = Uri.base.queryParameters['id'] ?? publicFirstCanvasId;
return Scaffold(
appBar: AppBar(title: Text('DUCK Demo')),
body: DuckCanvasWidget(canvasId: canvasId),
);
}
}
Creating Custom Components
You can create your own components by extending DuckComponent. Here's an example of a horizontal container component:
import 'package:duck/core/duck_component/duck_component.dart';
import 'package:duck/core/duck_component/duck_component_list_converter.dart';
import 'package:duckrunner/components/horizontal_container/horizontal_container_widget.dart';
import 'package:flutter/widgets.dart';
import 'package:json_annotation/json_annotation.dart';
part 'horizontal_container_data.g.dart';
@JsonSerializable()
class HorizontalContainerData extends DuckComponent {
static const String componentType = 'horizontalContainer';
@override
String get type => componentType;
@DuckComponentListConverter()
final List<DuckComponent> list;
HorizontalContainerData({required this.list});
@override
Widget buildWidget(BuildContext context) {
return HorizontalContainerWidget(
data: HorizontalContainerWidgetData.fromComponent(this),
);
}
factory HorizontalContainerData.fromJson(Map<String, dynamic> json) =>
_$HorizontalContainerDataFromJson(json);
@override
Map<String, dynamic> toJson() => _$HorizontalContainerDataToJson(this);
factory HorizontalContainerData.mock() {
return HorizontalContainerData(list: []);
}
}
Then create the widget implementation:
import 'package:duck/core/duck_component/duck_component.dart';
import 'package:duckrunner/components/horizontal_container/horizontal_container_data.dart';
import 'package:flutter/material.dart';
class HorizontalContainerWidgetData {
final List<DuckComponent> list;
HorizontalContainerWidgetData({required this.list});
factory HorizontalContainerWidgetData.fromComponent(
HorizontalContainerData component,
) {
return HorizontalContainerWidgetData(list: component.list);
}
}
class HorizontalContainerWidget extends StatelessWidget {
const HorizontalContainerWidget({super.key, required this.data});
final HorizontalContainerWidgetData data;
@override
Widget build(BuildContext context) {
return SizedBox(
width: double.infinity,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: data.list
.map((component) => component.buildWidget(context))
.toList(),
),
);
}
}
Finally, don't forget to register your custom component in main.dart:
Duck(
baseUrl: baseUrl,
components: {
HorizontalContainerData.componentType: HorizontalContainerData.fromJson,
},
);
How It Works
- Duck Builder: Create UI components/elements as JSON canvases using duck.flexxxlab.com
- Dynamic Updates: Update your UI in production without app store approval
- Public Components: Use pre-built components from the public library
- Custom Components: Create and register your own components in your app
- Minimal Updates: App updates are only needed when adding new component types
Code Coverage
This package has 98% code coverage, ensuring reliability and stability in production use.
License
See LICENSE file.
Libraries
- components/duck_component_registry
- components/duck_conatiner/duck_container_data
- components/duck_conatiner/duck_container_widget
- components/duck_section_header/duck_section_header_data
- components/duck_section_header/duck_section_header_widget
- components/duck_small_card/duck_small_card_data
- components/duck_small_card/duck_small_card_widget
- components/duck_unknown/duck_unknown
- core/context_extension
- core/dependency
- core/duck_canvas/duck_canvas_repository
- core/duck_canvas/duck_canvas_widget
- core/duck_component/duck_component
- core/duck_component/duck_component_list_converter
- core/network
- duck