0

I attemplted to start a local webserver on Android using Flutter app. I'm using shelf & shelf_static.

I've put index.html file in the assets folder of the root project folder. Then, I referenced it in the pubspec.yaml file.

assets:
  - assets/

Then, I made a button to call this function:

Future<void> _startShelfLocalhostServer() async {
  // Serve the `web` directory.
  var handler = createStaticHandler('assets', defaultDocument: 'index.html');

  // Create a Shelf cascade with the static file handler first, and the fallback handler second.
  var cascade = Cascade().add(handler).add(_echoRequest);

  // Start the server on port 8080.
  var server = await io.serve(cascade.handler, 'localhost', 8080);

  // Print the URL to the console.
  print('Server listening on ${server.address.host}:${server. Port}');
}

I expect that I can open the HTML file when I go to localhost:8080, but instead, I got this error:

[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Invalid argument(s): A directory corresponding to fileSystemPath "assets" could not be found

So, how do I fix it?

iqfareez
  • 555
  • 7
  • 21

1 Answers1

0

The createStaticHandler seems cannot read files from assets directory directly. So, I solved this issue by first copying the contents in assets to device storage.

/// Copy the html project folder from flutter assets to device directory
/// The shelf cannot open access from flutter assets
Future<List<String>> _copyAssetsToDocuments() async {
// Get the app documents directory.
final directory = await getExternalStorageDirectory();

// Get a handle to the asset bundle.
final bundle = rootBundle;

// Get a list of all assets in the 'assets' folder.
final assets = await bundle.loadString('AssetManifest.json');
var assetList = jsonDecode(assets) as Map<String, dynamic>;

// removed unwanted assets
assetList.removeWhere((key, value) =>
    key.startsWith('assets/app_reserved') || key.startsWith('packages'));

// print all asset that will be copied
for (final assetPath in assetList.keys) {
  debugPrint(assetPath);
}

List<String> copiedAssets = [];

// Copy each asset to the app documents directory.
for (final assetPath in assetList.keys) {
  final assetData = await bundle.load(assetPath);
  // remove the 'assets/' part from the path
  final correctedAssetPath = assetPath.replaceFirst('assets/', 'web/');
  final file = File('${directory!.path}/$correctedAssetPath');
  await file.create(recursive: true);
  await file.writeAsBytes(assetData.buffer.asUint8List());

  // record
  copiedAssets.add(correctedAssetPath);
}

The contents is now is written to Android > data > <my.package.name> > files > web

So, to serve the static assets, I changed the original code to:

// Serve the device directory.
final directory = await getExternalStorageDirectory();
var handler = createStaticHandler(p.join(directory!.path, 'web'),
    defaultDocument: 'index.html');
iqfareez
  • 555
  • 7
  • 21