Skip to content

Commit

Permalink
refactor: replace plain strings with EvcLoaders
Browse files Browse the repository at this point in the history
  • Loading branch information
wrbl606 committed Jun 9, 2024
1 parent d12ebff commit 7182234
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 146 deletions.
4 changes: 1 addition & 3 deletions examples/code_push_app/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ class MyApp extends StatelessWidget {
// Load a potential hot-swap update to be used by our app. If the URI responds with
// an error, it will be treated as no update present.
return HotSwapLoader(
// Updates can be loaded from the network using http:// and https://,
// or from local files/Flutter assets using file:// and asset:// respectively.
uri: 'asset://assets/hot_update.evc',
loader: AssetEvcLoader.fromString('asset://assets/hot_update.evc'),
strategy: HotSwapStrategy.immediate,
child: MaterialApp(
title: 'Flutter Demo',
Expand Down
191 changes: 49 additions & 142 deletions lib/src/flutter_eval.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'dart:math';
// ignore: unnecessary_import
Expand Down Expand Up @@ -188,7 +189,7 @@ class _CompilerWidgetState extends State<CompilerWidget> {
/// A [RuntimeWidget] loads and runs EVC bytecode from a file, asset, or URL
/// at runtime and displays the returned [Widget].
///
/// [uri] is the URI to the EVC bytecode. Use the file:// scheme to load
/// [loader] is the URI to the EVC bytecode. Use the file:// scheme to load
/// from a local file, the asset:// scheme to load from an asset, or the http://
/// or https:// scheme to load from a URL.
///
Expand Down Expand Up @@ -218,17 +219,18 @@ class _CompilerWidgetState extends State<CompilerWidget> {
/// [loading] is a widget that is displayed while the EVC bytecode is loading.
///
class RuntimeWidget extends StatefulWidget {
const RuntimeWidget(
{required this.uri,
required this.library,
required this.function,
this.args = const [],
this.loading,
this.onError,
this.permissions = const [],
super.key});
const RuntimeWidget({
required this.loader,
required this.library,
required this.function,
this.args = const [],
this.loading,
this.onError,
this.permissions = const [],
super.key,
});

final Uri uri;
final EvcLoader loader;
final String library;
final String function;
final List<dynamic> args;
Expand All @@ -252,47 +254,19 @@ class _RuntimeWidgetState extends State<RuntimeWidget> {
super.initState();

try {
final scheme = widget.uri.scheme;
if (scheme == 'file') {
_loadFromFile();
} else if (scheme == 'asset') {
_loadFromAsset();
} else if (scheme == 'http' || scheme == 'https') {
_loadFromUrl();
} else {
throw 'Unsupported scheme: ${widget.uri.scheme}';
}
_loadFrom(widget.loader);
} catch (e, stackTrace) {
if (!_setError(e, stackTrace, false)) {
rethrow;
}
}
}

void _loadFromFile() {
final file = File(widget.uri.path);
final bytecode = file.readAsBytesSync();
_setup(bytecode);
}

void _loadFromAsset() async {
Future<void> _loadFrom(EvcLoader loader) async {
try {
final asset = widget.uri.path;
final bytecode = await rootBundle.load(asset);
_setup(bytecode);
} catch (e, stackTrace) {
if (!_setError(e, stackTrace)) {
rethrow;
}
}
}

void _loadFromUrl() async {
try {
final response = await http.get(widget.uri);
_setup(response.bodyBytes);
} catch (e, stackTrace) {
if (!_setError(e, stackTrace)) {
_setup(await loader.bytecode);
} catch (e, s) {
if (!_setError(e, s)) {
rethrow;
}
}
Expand Down Expand Up @@ -390,23 +364,24 @@ class _RuntimeWidgetState extends State<RuntimeWidget> {
/// You can grant permissions to the dart_eval runtime using [permissions].
///
class EvalWidget extends StatefulWidget {
const EvalWidget(
{required this.packages,
required this.assetPath,
required this.library,
this.uri,
this.function = 'main',
this.args = const [],
this.loading,
this.onError,
this.permissions = const [],
super.key});
const EvalWidget({
required this.packages,
required this.assetPath,
required this.library,
this.loader,
this.function = 'main',
this.args = const [],
this.loading,
this.onError,
this.permissions = const [],
super.key,
});

final Map<String, Map<String, String>> packages;
final String assetPath;
final String library;
final String function;
final Uri? uri;
final EvcLoader? loader;
final Widget? loading;
final List<dynamic> args;
final EvalErrorBuilder? onError;
Expand Down Expand Up @@ -434,20 +409,10 @@ class _EvalWidgetState extends State<EvalWidget> {
compiler = Compiler()..addPlugin(flutterEvalPlugin);
_recompile(false);
} else {
if (widget.uri == null) {
_loadFromAsset(widget.assetPath);
} else {
final scheme = widget.uri!.scheme;
if (scheme == 'file') {
_loadFromFile();
} else if (scheme == 'asset') {
_loadFromAsset(widget.uri!.toString());
} else if (scheme == 'http' || scheme == 'https') {
_loadFromUrl();
} else {
throw 'Unsupported scheme: ${widget.uri!.scheme}';
}
}
final loader = widget.loader != null
? widget.loader!
: AssetEvcLoader.fromString(widget.assetPath);
_loadFrom(loader);
}
} catch (e, stackTrace) {
if (!_setError(e, stackTrace, false)) {
Expand Down Expand Up @@ -482,29 +447,11 @@ class _EvalWidgetState extends State<EvalWidget> {
}
}

void _loadFromFile() {
final file = File(widget.uri!.path);
final bytecode = file.readAsBytesSync();
_setup(bytecode);
}

void _loadFromAsset(String assetPath) async {
Future<void> _loadFrom(EvcLoader loader) async {
try {
final bytecode = await rootBundle.load(assetPath);
_setup(bytecode);
} catch (e, stackTrace) {
if (!_setError(e, stackTrace)) {
rethrow;
}
}
}

void _loadFromUrl() async {
try {
final response = await http.get(widget.uri!);
_setup(response.bodyBytes);
} catch (e, stackTrace) {
if (!_setError(e, stackTrace)) {
_setup(await loader.bytecode);
} catch (e, s) {
if (!_setError(e, s)) {
rethrow;
}
}
Expand Down Expand Up @@ -593,7 +540,7 @@ Future<Uri> _writeBytesToPath(String path, Uint8List bytes) async {
/// root of your app.
class HotSwapLoader extends StatefulWidget {
const HotSwapLoader({
required this.uri,
required this.loader,
required this.child,
this.strategy,
this.cacheFilePath,
Expand All @@ -605,8 +552,9 @@ class HotSwapLoader extends StatefulWidget {

final Widget child;

/// URI of the bytecode to load. This can be a http/https, file, or asset URI.
final String uri;
/// Loader of the bytecode. See [EvcLoader], [FileEvcLoader],
/// [AssetEvcLoader], [HttpEvcLoader].
final EvcLoader loader;

/// Callback to run if an error occurs. Network errors do not call this.
final EvalErrorCallback? onError;
Expand Down Expand Up @@ -667,16 +615,7 @@ Multiple HotSwapLoaders in the widget tree are not supported.
if (_strategy != HotSwapStrategy.immediate) {
_loadFromCache();
}
final scheme = Uri.parse(widget.uri).scheme;
if (scheme == 'file') {
_loadFromFile();
} else if (scheme == 'asset') {
_loadFromAsset();
} else if (scheme == 'http' || scheme == 'https') {
_loadFromUrl();
} else {
throw 'Unsupported scheme: ${Uri.parse(widget.uri).scheme}';
}
_loadFrom(widget.loader);
} catch (e, stackTrace) {
if (!_setError(e, stackTrace, false)) {
rethrow;
Expand Down Expand Up @@ -709,51 +648,19 @@ Multiple HotSwapLoaders in the widget tree are not supported.
}
}

Future<void> _loadFromFile() async {
try {
debugPrint('Loading hot update from ${widget.uri}');
final loader = FileEvcLoader.fromString(widget.uri);
final bytecode = await loader.bytecode;
_setup(bytecode);
} catch (e, stackTrace) {
if (!_setError(e, stackTrace)) {
rethrow;
}
}
}

Future<void> _loadFromAsset() async {
Future<void> _loadFrom(EvcLoader loader) async {
try {
debugPrint('Loading hot update from ${widget.uri}');
final loader = AssetEvcLoader.fromString(widget.uri);
final bytecode = await loader.bytecode;
_setup(bytecode);
} catch (e, stackTrace) {
if (!_setError(e, stackTrace)) {
rethrow;
}
}
}

Future<void> _loadFromUrl() async {
try {
debugPrint('Attempting to load hot update from ${widget.uri}');
final loader = HttpEvcLoader(uri: Uri.parse(widget.uri));
final bytecode = await loader.bytecode;
_setup(bytecode);
} on http.ClientException catch (_) {
debugPrint('No update found at address (network request failed)');
// ignored
} catch (e, stackTrace) {
if (!_setError(e, stackTrace)) {
_setup(await loader.bytecode);
} catch (e, s) {
if (!_setError(e, s)) {
rethrow;
}
}
}

void _setup(TypedData bytecode, {bool fromCache = false}) {
if (fromCache == false && _strategy != HotSwapStrategy.immediate) {
debugPrint('Cacheing hot update');
debugPrint('Caching hot update');
_saveToCache(bytecode);
}
if (fromCache == false &&
Expand Down
2 changes: 1 addition & 1 deletion lib/src/loaders/evc_loader.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
part of '../flutter_eval.dart';

abstract class EvcLoader {
Future<TypedData> get bytecode;
FutureOr<TypedData> get bytecode;
}

class EvcLoaderException implements Exception {
Expand Down

0 comments on commit 7182234

Please sign in to comment.