How top app updating the UI component without app update from play store?

159 Views Asked by At

I am curious about how major top social media apps updating their UI without user interaction. I want to implement same in flutter mobile app. I have gone through dynamic feature (On-Demand feature in android)

Deferred Components

But, Don't know do play store allow to upload only specific component.

If anyone have idea about it, Please help me.

2

There are 2 best solutions below

2
On

What you are looking for, are remote widgets and Server-Driven UI.

Using Server-Driven UI and Remote Widgets in Flutter

What is Server-Driven UI?

Server-Driven UI is an approach where a server determines the structure and appearance of the user interface, dynamically sending instructions to the client at runtime on how to render it. This can be particularly useful in Flutter applications to achieve a more flexible and scalable UI.

Implementing Server-Driven UI in Flutter

To implement Server-Driven UI in Flutter, you can use the official rfw (Remote Flutter Widgets) package by flutter.dev.

Firstly, create a .rfwtxt file with your remote widget that has the following structure:

import core.widgets;
import core.material;

widget Counter = Scaffold(
  appBar: AppBar(title: Text(text: "Counter Demo")),
  body: Center(
    child: Column(
      mainAxisAlignment: "center",
      children: [
        Text(text: 'You have pushed the button this many times:', textAlign: "center"),
        Text(text: data.counter, style: {
          fontSize: 36.0,
        }),
      ],
    ),
  ),
  floatingActionButton: FloatingActionButton(
    onPressed: event "increment" { },
    tooltip: "Increment",
    child: Icon(icon: 0xE047, fontFamily: "MaterialIcons"),
  ),
);

Then, run encoder.dart in the directory of your .rfwtxt file to encode it into a .rfw file, which can than be used by the package (you need to adapt the file names accordingly).

Next, prepare your runtime and download the previously created file within your code:

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import 'package:rfw/rfw.dart';

class _ExampleState extends State<Example> {
  final Runtime _runtime = Runtime();
  final DynamicContent _data = DynamicContent();

  final String url = "your/url/to/your/rfwfile"; // Change this to your server url
  bool _ready = false;
  int _counter = 0;

  @override
  void initState() {
    super.initState();
    _runtime.update(const LibraryName(<String>['core', 'widgets']), createCoreWidgets());
    _runtime.update(const LibraryName(<String>['core', 'material']), createMaterialWidgets());
    _updateData();
    _updateWidgets();
  }

  void _updateData() {
    _data.update('counter', _counter.toString());
  }

  Future<void> _updateWidgets() async {
    final Directory home = await getApplicationSupportDirectory();
    final File rfwFile = File(path.join(home.path, 'my_widget.rfw'));
    if (rfwFile.existsSync()) {
      try {
        _runtime.update(const LibraryName(<String>['main']), decodeLibraryBlob(await rfwFile.readAsBytes()));
        setState(() {
          _ready = true;
        });
      } catch (e, stack) {
        FlutterError.reportError(FlutterErrorDetails(exception: e, stack: stack));
      }
    }
    print('Fetching remote widget: $url'); // ignore: avoid_print
    final HttpClientResponse client = await (await HttpClient().getUrl(Uri.parse(url))).close(); // Download the file
    await rfwFile.writeAsBytes(await client.expand((List<int> chunk) => chunk).toList()); // Save the contents of the response to a local file
  }

  @override
  Widget build(BuildContext context) {
    Widget? result;
    if (_ready) {
      result = RemoteWidget(
        runtime: _runtime,
        data: _data,
        widget: const FullyQualifiedWidgetName(LibraryName(<String>['main']), 'Counter'),
        onEvent: (String name, DynamicMap arguments) {
          if (name == 'increment') {
            _counter += 1;
            _updateData();
          }
        },
      );
    }
    
    return result ?? Container();
  }
}

Limitations

Once you realize that you can ship UI (and maybe logic, e.g. using Wasm; see the example below) you will slowly be tempted to move your whole application to this model.

This won't work.

Flutter proper lets you create widgets for compelling UIs with gestures and animations and so forth. With RFW you can use those widgets, but it doesn't let you create those widgets.

For example, you don't want to use RFW to create a UI that involves page transitions. You don't want to use RFW to create new widgets that involve drag and drop. You don't want to use RFW to create widgets that involve custom painters.

Rather, RFW is best suited for interfaces made out of prebuilt components. For example, a database front-end could use this to describe bespoke UIs for editing different types of objects in the database. Message-of-the-day announcements could be built using this mechanism. Search interfaces could use this mechanism for rich result cards.

RFW is well-suited for describing custom UIs from a potentially infinite set of UIs that could not possibly have been known when the application was created. On the other hand, updating the application's look and feel, changing how navigation works in an application, or adding new features, are all changes that are best made in Flutter itself, creating a new application and shipping that through normal update channels.

RFW is ideal for creating dynamic custom UIs with prebuilt components. However, it falls short in handling complex UI elements like gestures, animations, and custom painters. For fundamental UI changes, navigation adjustments, or new features, it's recommended to utilize Flutter itself. RFW is best suited for adapting to unforeseen UI requirements within an evolving application.

Conclusion

This will fetch your .rfw file from your server at runtime, load it into your app and render it, allowing your application to dynamically adapt to server-side changes in the user interface. You could also implement code that periodically fetches the file from your database or listens for changes, so your UI stays up-to-date.

For more information, refer to the docs.

1
On

You can try shorebird to push app updates directly to users' devices