How to display an HTML asset file? - dart

I have an .html file in my assets directory.
How can I display / render it in Flutter?

The package from the Flutter Team was released yesterday:
webview_flutter
How to load local asset:
Add the file to the project and update your pubspec:
//...
assets:
//...
- assets/yourFile.html
//...
In your widget:
child: FutureBuilder<String>(
future: LocalLoader().loadLocal(),
builder: (context, snapshot) {
if (snapshot.hasData) {
// return Text("${snapshot.data}");
return WebView(
initialUrl: new Uri.dataFromString(snapshot.data,
mimeType: 'text/html')
.toString(),
// maybe you Uri.dataFromString(snapshot.data, mimeType: 'text/html', encoding: Encoding.getByName("UTF-8")).toString()
javascriptMode: JavascriptMode.unrestricted,
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return CircularProgressIndicator();
},
),
Loading the text from the file:
class LocalLoader {
Future<String> loadLocal() async {
return await rootBundle.loadString('assets/yourFile.html');
}
}

You can use the flutter_webview_plugin this should work with local files too.
It works both on Android and iOS
Dart
var flutterWebviewPlugin = new FlutterWebviewPlugin();
flutterWebviewPlugin.launch("https://flutter.io");
await flutterWebviewPlugin.onDestroy.first;
Android Manifest
<activity android:name="com.flutter_webview_plugin.WebviewActivity"
android:parentActivityName=".MainActivity"/>

Unzip the apk package. I found the reason: path is wrong
For Android:
"assets/test.html" == "file:///android_asset/flutter_assets/assets/test.html"
so, just like this:
WebView(
initialUrl: "file:///android_asset/flutter_assets/assets/test.html",
javascriptMode: JavascriptMode.unrestricted,
)
you can load assets/test.html.

You can try my flutter_inappbrowser plugin (EDIT: it has been renamed to flutter_inappwebview), that is a Flutter Plugin that allows you to add an inline webview integrated with the widget tree or open an in-app browser window!
You can use the InAppWebView widget class or the InAppBrowser class and use its webview controller.
In your case, you can use the InAppWebViewController.loadFile method.
To be able to load your local files (html, js, css, etc.), you need to add them in the assets section of the pubspec.yaml file, otherwise they cannot be found!
Example of a pubspec.yaml file:
...
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
assets:
- assets/index.html
- assets/css/
- assets/images/
...
Then, call the method in your business logic:
inAppWebViewController.loadFile("assets/index.html");
or
InAppWebView(
onWebViewCreated: (InAppWebViewController controller) {
var uri = "assets/index.html"
controller.loadFile(assetFilePath: uri);
},
),

If you just need to render simple HTML tags (p,img,a,h1,etc.) you could try this flutter_html plugin
It's not mature enough for production yet IMHO but worth considering / contributing.

Related

xCode error causing app to crash immediately after launch

I am having an issue with my flutter app on iOS. It is working well on android and on web but I am getting this error on iOS. I come from a web dev background and do not have a lot of experience in iOS.
I am getting the error below and have zero idea of where to even begin. It appears to be an issue with one of the plugins... but I honestly have not idea. Any help on a direction would be incredibly helpful.
Error from xCode
This is a fairly large app
Here is my pubspec.
version: 1.6.0+18
environment:
sdk: ^2.7.0
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
http: ^0.13.0
responsive_builder: ^0.3.0
bloc: ^7.0.0
flutter_bloc: ^7.0.0
equatable: ^2.0.0
meta: ^1.1.6
hive: ^2.0.0
hive_flutter: ^1.0.0
flutter_appauth: ^0.9.2+6
intl: ^0.17.0
table_calendar: ^2.3.3
js: ^0.6.0
floating_action_bubble: ^1.1.2
badges: ^1.1.6
plaid_flutter:
git:
url: https://github.com/cwesterhold/plaid_flutter
ref: master
font_awesome_flutter: ^8.11.0
auth0_spa:
git:
url: https://github.com/cwesterhold/auth0-spa-flutter
ref: master
multiple_select:
git:
url: https://github.com/cwesterhold/multiple_select
ref: master
#catcher: ^0.6.2
flutter_localizations:
sdk: flutter
#package_info_plus: ^1.0.0
purchases_flutter: ^3.0.0
url_launcher: ^6.0.0
syncfusion_flutter_charts: ^18.4.47
progress_indicator: ^0.1.1
uuid:
dev_dependencies:
flutter_test:
sdk: flutter
hive_generator:
build_runner: ^1.10.0
flutter_launcher_icons: ^0.9.0
flutter_icons:
image_path: 'assets/ic_launcher.png'
android: false
ios: true
#flutter pub run flutter_launcher_icons:main
flutter:
uses-material-design: true
assets:
- assets/
fonts:
- family: Comfortaa
fonts:
- asset: fonts/Comfortaa-VariableFont_wght.ttf
Here is the main
import 'dart:async';
import 'package:clicket/bloc/theme/theme.dart';
import 'package:clicket/models/errorModel.dart';
import 'package:clicket/screens/login/loginMain.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
void main() async {
bool isInDebugMode = false;
assert(isInDebugMode = true);
ErrorModel errorModel = ErrorModel();
WidgetsFlutterBinding.ensureInitialized();
bool isDev = false;
assert(isDev = true);
if (isDev == true) {
runApp(Clicket());
} else {
runZoned<Future<void>>(
() async {
runApp(Clicket());
},
onError: (dynamic error, StackTrace stackTrace) {
errorModel.reportError(error, stackTrace);
// Send report
},
);
}
}
class Clicket extends StatefulWidget {
#override
_ClicketState createState() => _ClicketState();
}
class _ClicketState extends State<Clicket> {
var s;
ThemeBloc _themeBloc = ThemeBloc();
final navigatorKey = GlobalKey<NavigatorState>();
#override
Widget build(BuildContext context) {
// make sure to close out all blocs to prevent memory leaks
#override
void dispose() {
_themeBloc.close();
super.dispose();
}
// Includes all blocs so they are available across the entire application
return BlocProvider<ThemeBloc>(
create: (context) => _themeBloc,
child: BlocBuilder<ThemeBloc, ThemeState>(
builder: (context, state) {
if (state is ThemeStart) {
_themeBloc.add(CheckTheme());
} else if (state is ThemeLight) {
s = state.themeData;
} else if (state is ThemeDark) {
s = state.themeData;
}
return MaterialApp(
navigatorKey: navigatorKey,
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale('en', 'US'),
],
debugShowCheckedModeBanner: false,
title: "Clicket - Personal Budgeting",
onGenerateRoute: (rt) {
return MaterialPageRoute(
builder: (context) {
return LoginMain();
},
);
},
theme: s,
);
},
),
);
}
}
If I remove the LogIn route and rerun it in xCode, I get this.
Second xCode error
Which is just as cryptic....
When I run things through android studio it doesnt give any insights at all...
NOthing in Android Studio
Even if I remove everything except for a basic scaffold, I still get the same second error as above. But if I create a new project with the same pubspec, it works just fine.
Any help would be greatly appreciated.
So I found the issues...
it had to do with a bad line in the info.plist file.
I had this..
<key>NSAppTransportSecurity</key>
<true/>
and it should be this...
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Lesson learned - dont just blindly copy code you find on the internet :-)

How do I open an external url in flutter web in new tab or in same tab

I have a simple web app I have created with flutter web. I would like to know how I can open new an external url either in a new tab or in the same tab in my flutter web app. say I want to open the url https://stackoverflow.com/questions/ask
I think you want this — dart:js enables interoperability between Dart and JS —:
import 'dart:js' as js;
// ...
FlatButton(
child: Text('Button'),
onPressed: () {
js.context.callMethod('open', ['https://stackoverflow.com/questions/ask']);
},
)
One simple way is to just create a button and use dart:html's window.open() method:
import 'dart:html' as html;
// ...
html.window.open('https://stackoverflow.com/questions/ask', 'new tab');
The name parameter — which I left as 'new tab' — refers to the new tab's window name, about which you can learn more from MDN's documentation.
https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_web
url_launcher has been the solution for android and ios, recently it added support for web.
You can use the url_launcher plugin
Then in your code
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher_string.dart';
void main() {
runApp(Scaffold(
body: Center(
child: RaisedButton(
onPressed: _launchURL,
child: Text('Show Flutter homepage'),
),
),
));
}
_launchURL() async {
const url = 'https://flutter.io';
if (await canLaunchUrlString(url)) {
await launchUrlString(url);
} else {
throw 'Could not launch $url';
}
}
Example taken from the package site
As of url_launcher: ^6.1.0 The plugin introduces support for webOnlyWindowName property and few Other new apis like launchUrl, For web support now you longer have to depend on dart:html You can simply declare the below function
Future<void> launchLink(String url, {bool isNewTab = true}) async {
await launchUrl(
Uri.parse(url),
webOnlyWindowName: isNewTab ? '_blank' : '_self',
);
}
and use it like this
onTap:(){
launchLink('https://stackoverflow.com/questions/ask', isNewTab: true)
}
Answered here https://stackoverflow.com/a/56656885/361832
Flutter Web does not support plugins (yet), so you have to use replacements from dart:html
https://api.dartlang.org/stable/2.4.0/dart-html/Window/open.html window.open(url, 'tab');
or
https://api.dartlang.org/stable/2.4.0/dart-html/Window/location.html window.location.assign(url);
Extending the answer of #xuyanjun which works fine when to want to open an external link from flutter web to a new tab. But if you want to open an external link to the website in the same tab in which the flutter web app is currently running.
then you can do it like this.
import 'dart:js' as js;
// ...
FlatButton(
child: Text('Button'),
onPressed: () {
js.context.callMethod('open', ['https://blup.in/','_self']); //<= find explanation below
},
)
Explanation :- dart:js package from flutter provide the functionality to call web-specific functions like open function from flutter and all the strings in the list are parameter which are passed to the function refer this.
So if you want to open new tab then not need to pass seconds parameter but if you wan to open in same tab then pass _self as second parameter.
You can use the below code to launch the URL in the same tab.
window.top.location.href = '<your url>'
Must import
import 'dart:html';
package url_launcher now has web support.
just import url_launcher_web, and url_launcher to your pubspec.yaml
import 'package:url_launcher/url_launcher.dart';
const String _url = 'https://flutter.dev';
void _launchURL() {
launch(_url);
}
To launch URL you need to import url_launcher with latest version and you are good to launch URL through following code:
//Launch Github
_launchGithub() async {
const url = 'https://github.com/Pranav2918'; //Paste your URL string here
if (await canLaunchUrlString(url)) {
await launchUrlString(url);
} else {
throw 'Could not launch $url';
}
}
For non string URLs:
Another approach :
final Uri _url = Uri.parse('https://flutter.dev');
Future<void> _launchUrl() async {
if (!await launchUrl(_url)) {
throw 'Could not launch $_url';
}
}
Happy Fluttering !!

Check if asset exist in flutter

How I can check if a specific asset exists in Flutter. I'm trying to load some images and sound files and I need to handle the case when these assets do not exist.
I need to check the existence because I have audio files and images for numbers from 1 to 1000. When I build my widgets I use a loop from 1 to 1000 to build it. and there are possibilities that the required file ( the image or the sound for the current number ) does not exist in the assets.
you can try my solution, if you use a simple Image.asset Widget:
Image.asset(
'assets/image.jpg',
errorBuilder: (BuildContext context, Object exception, StackTrace stackTrace) {
return Image.network('path');})
Following Raouf suggestion I handled the case where the assets not exist.
Image loader widget:
Future<Image> _buildImage() async {
String path = "assets/images/contents/${content.id}.jpg";
return rootBundle.load(path).then((value) {
return Image.memory(value.buffer.asUint8List());
}).catchError((_) {
return Image.asset(
"assets/images/null.png",
height: 250.0,
);
});
}
Using the Image widget inside my build method:
FutureBuilder(
future: _buildImage(),
builder: (BuildContext context, AsyncSnapshot<Image> snapshot) {
if (snapshot.connectionState == ConnectionState.done)
return snapshot.data;
else
return Image.asset("assets/images/null.png");
},
),
),
I assume that you are using the AssetBundle class to load your data using the load method which takes ByteData, and when you use this method, it will throws an exception if the asset is not found.
For someone that Flutter IO Dev answer did not work because the exception still appears, this worked for me:
Future<Widget> getDevIcon(String path) async {
try {
await rootBundle.load(path);
return Image.asset(path);
} catch (_) {
return SizedBox.shrink();
}
}

How to get bundle id in flutter

I used the below method to get the app name and packageName but I need Bundle id for iPhone users.
I want to share an app link.
I did it in android but on iPhone, I need bundle id.
Future<Null> _initPackageInfo() async {
final PackageInfo info = await PackageInfo.fromPlatform();
setState(() {
_packageInfo = info;
packageName = info.packageName;
appName = info.appName;
buildNumber = info.buildNumber;
});
}
To find the project name manually, you can look in AndroidManifest.xml or in Info.plist.
Android
In Android the package name is in the AndroidManifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
...
package="com.example.appname">
iOS
In iOS the package name is the bundle identifier in Info.plist:
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
which is found in Runner.xcodeproj/project.pbxproj:
PRODUCT_BUNDLE_IDENTIFIER = com.example.appname;
See also
How to change package name in flutter?
In iOS portion of a Flutter project Product Bundle Identifier is in project.pbxproj file in path:
[your-flutter-project-dir]\ios\Runner.xcodeproj\project.pbxproj
and that is specified as following:
PRODUCT_BUNDLE_IDENTIFIER = com.app.flutter.example;
Note in that this value is same as Android Package Name in Flutter projects.
If you just need to get the IOS bundle ID manually, here is how
In Android Studio select the root folder (ex. flutte_name)
In the taskbar go to Tools>>Flutter>>Open IOS Modules in Xcode
In Xcode open Runner and under Identity/Bundle Identifier there is your ID
You probably want to update it to a custom name rather than com.example.appName anyways so you can check out this package called change_app_name on pub.dev here https://pub.dev/packages/change_app_package_name
Super simple I just did it myself. Add the package to your pubspec file and than in a terminal in the root folder type in "flutter pub run change_app_package_name:main com.company.app" change the last part to whatever you want and it will update your whole project with the new name you chose
what i see you have already get the package name by info.packageName; and appName info.appName;
try request to http://itunes.apple.com/search?media=software&country={countryID}&term={appName}
you can test by curl instead of browser ( this will return file.txt )
curl --location -g --request GET 'http://itunes.apple.com/search?media=software&country=id&term=appName' \
--header 'Accept: application/json' | jq
in other solution some people can get it from
// if doesnt work try with these lookup
itunes.apple.com/lookup?bundleId=com.apple.Pages
| jq // these for make response beutifull
response json
....
....
bundleId: '212312xxxxxx'
trackViewUrl: 'https://apps.apple.com/id/app/appName/id212312xxxxxx'
....
....
i use these concept to self update from backend ( SplashScreen / check app version ) will popUP alert forced user to go to the market place.
Use get_version package . It's the easiest way
Installing :
dependencies:
get_version: any
Usage:
String projectAppID;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
projectAppID = await GetVersion.appID;
} on PlatformException {
projectAppID = 'Failed to get app ID.';
}
You can use it as String inside anything you want like Text widget etc ...
Another extract of get_version in a small application :
import 'package:get_version/get_version.dart';
class _MyAppState extends State<MyApp> {
String _projectAppID = '';
#override
initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
initPlatformState() async {
String projectAppID;
try {
projectAppID = await GetVersion.appID;
} catch (e) {
projectAppID = 'Failed to get app ID.';
}
setState(() {
_projectAppID = projectAppID;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ListTile(
leading: new Icon(Icons.info),
title: const Text('App ID'),
subtitle: new Text(_projectAppID),
),
),
);
}
}
Output :

How to play a custom sound in Flutter?

I was able to play a simple sound with this line of code:
SystemSound.play(SystemSoundType.click);
How can I play a customized sound?
Let's say a short mp3
Simple solution for playing a file already defined in assets is using AudioCache.
Library: https://pub.dartlang.org/packages/audioplayers.
More about AudioCache
After adding library to pubspec.yaml, import required class:
import 'package:audioplayers/audio_cache.dart';
add an asset in the same file and place the file with sound to assets folder (if you don't have this folder, create it)
assets:
- assets/sound_alarm.mp3
then add this code:
static AudioCache player = new AudioCache();
const alarmAudioPath = "sound_alarm.mp3";
player.play(alarmAudioPath);
An example here
Thanks for checking out Flutter!
Flutter SDK today (as of May 5, 2017) doesn't have built-in support to play and control arbitrary audio. However, we designed our plugin system to support it.
This plugin adds audio support to Flutter: https://pub.dartlang.org/packages/audioplayer
From the plugin's README:
Future play() async {
final result = await audioPlayer.play(kUrl);
if (result == 1) setState(() => playerState = PlayerState.playing);
}
// add a isLocal parameter to play a local file
Future playLocal() async {
final result = await audioPlayer.play(kUrl);
if (result == 1) setState(() => playerState = PlayerState.playing);
}
Future pause() async {
final result = await audioPlayer.pause();
if (result == 1) setState(() => playerState = PlayerState.paused);
}
Future stop() async {
final result = await audioPlayer.stop();
if (result == 1) {
setState(() {
playerState = PlayerState.stopped;
position = new Duration();
});
}
}
The audioplayers works (from https://medium.com/#bennett4/adding-custom-sound-effects-to-a-flutter-mobile-app-41594f1f3305):
(1) Add the library to your pubspec.yaml: audioplayers: ^0.15.1
(2) In pubspec.yaml under flutter add the reference to your assets file:
flutter
assets:
- assets/yes.mp3
MAKE SURE it is under the assets folder. It does not work when it is in a subfolder. For example, something like: - assets/sounds/yes.mp3 will not work. Just put your audio file in the assets folder, not in its subfolder
(3) import the library in your app as: import package:audioplayers/audioplayers.dart;
(4) then define this function:
Future<AudioPlayer> playLocalAsset() async {
AudioCache cache = new AudioCache();
//At the next line, DO NOT pass the entire reference such as assets/yes.mp3. This will not work.
//Just pass the file name only.
return await cache.play("yes.mp3");
}
(5) call the function whenever you need to play a sound: await playLocalAsset();
Null-safe code:
Add dependency to your pubspec.yaml file,
dependencies:
audioplayers: ^0.19.0
Add audio file path to your pubspec.yaml file.
flutter:
assets:
- assets/audio/my_audio.mp3
Run flutter pub get
Full code:
class HomePage extends StatelessWidget {
final AudioCache _audioCache = AudioCache(
prefix: 'audio/',
fixedPlayer: AudioPlayer()..setReleaseMode(ReleaseMode.STOP),
);
#override
Widget build(BuildContext context) {
return Scaffold(
body: ElevatedButton(
onPressed: () => _audioCache.play('my_audio.mp3'),
child: Text('Play Audio'),
),
);
}
}
[Answer updated: this approach doesn't work, see comments]
You can use the video_player plugin maintained by the Flutter team. It can reproduce many kinds of media across platforms, including sound files. More specifically, you may want to use the the VideoPlayerController class.
eg. _controller = VideoPlayerController.network('https://www.example.com/soundsFile.wav');
_controller.play();

Resources