import 'dart:io'; import 'dart:async'; import 'dart:typed_data'; import 'package:flutter/services.dart' show rootBundle; import 'mime_type_resolver.dart'; ///This class allows you to create a simple server on `http://localhost:[port]/` in order to be able to load your assets file on a server. The default [port] value is `8080`. class InAppLocalhostServer { bool _started = false; HttpServer? _server; int _port = 8080; InAppLocalhostServer({int port = 8080}) { this._port = port; } ///Starts the server on `http://localhost:[port]/`. /// ///**NOTE for iOS**: For the iOS Platform, you need to add the `NSAllowsLocalNetworking` key with `true` in the `Info.plist` file (See [ATS Configuration Basics](https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW35)): ///```xml ///NSAppTransportSecurity /// /// NSAllowsLocalNetworking /// /// ///``` ///The `NSAllowsLocalNetworking` key is available since **iOS 10**. Future start() async { if (this._started) { throw Exception('Server already started on http://localhost:$_port'); } this._started = true; var completer = Completer(); runZonedGuarded(() { HttpServer.bind('127.0.0.1', _port).then((server) { print('Server running on http://localhost:' + _port.toString()); this._server = server; server.listen((HttpRequest request) async { Uint8List body = Uint8List(0); var path = request.requestedUri.path; path = (path.startsWith('/')) ? path.substring(1) : path; path += (path.endsWith('/')) ? 'index.html' : ''; try { body = (await rootBundle.load(path)).buffer.asUint8List(); } catch (e) { print(e.toString()); request.response.close(); return; } var contentType = ContentType('text', 'html', charset: 'utf-8'); if (!request.requestedUri.path.endsWith('/') && request.requestedUri.pathSegments.isNotEmpty) { var mimeType = MimeTypeResolver.lookup(request.requestedUri.path); if (mimeType != null) { contentType = _getContentTypeFromMimeType(mimeType); } } request.response.headers.contentType = contentType; request.response.add(body); request.response.close(); }); completer.complete(); }); }, (e, stackTrace) => print('Error: $e $stackTrace')); return completer.future; } ///Closes the server. Future close() async { if (this._server == null) { return; } await this._server!.close(force: true); print('Server running on http://localhost:$_port closed'); this._started = false; this._server = null; } ///Indicates if the server is running or not. bool isRunning() { return this._server != null; } ContentType _getContentTypeFromMimeType(String mimeType) { final contentType = mimeType.split('/'); String? charset; if (_isTextFile(mimeType)) { charset = 'utf-8'; } return ContentType(contentType[0], contentType[1], charset: charset); } bool _isTextFile(String mimeType) { final textFile = RegExp(r'^text\/|^application\/(javascript|json)'); return textFile.hasMatch(mimeType); } }