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.

98% Coverage

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

  1. Duck Builder: Create UI components/elements as JSON canvases using duck.flexxxlab.com
  2. Dynamic Updates: Update your UI in production without app store approval
  3. Public Components: Use pre-built components from the public library
  4. Custom Components: Create and register your own components in your app
  5. 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.