Check if asset exist in flutter - dart

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();
}
}

Related

Flutter won't initialise a video from disk

I've downloaded a video to disk but it won't initialise using Video Player (https://pub.dev/packages/video_player).
final future = Downloader.shared
.getVideoPathFor(
url: url,
themeName: themeName,
)
.then(
(value) {
dLog('file path: $value');
final file = File(value);
final videoController = VideoPlayerController.file(file);
return videoController.initialize().then(
(value) {
dLog('video controller initialized');
return videoController;
},
);
},
);
It downloads the file fine and the file path becomes something like:
Application/9E6FD935-A424-4C1E-99CC-D5834448E45E/Library/Caches/videos/Clean water/clean_water_video.mp4
So I know the file exists and I can see it in Finder if I run this on Simulator.
If I use VideoController.contentUri() then it appears to work but it tells me I'm not allowed to use that on iOS and I can only use it on Android.
I know I can use VideoController.network() but I can't keep downloading the same video over and over across multiple screens like this.
What am I doing wrong?
Even when I do load the video like this (which I got from this video: https://youtu.be/uz4xRnE-UIw?t=596):
final filePath =
"<path>/Caches/videos/Clean water/clean_water_video.mp4";
final file = File(filePath);
controller = VideoPlayerController.file(file)
..addListener(() {
setState(() {});
})
..setLooping(true)
..initialize().then((value) {
controller.play();
});
the initialise never happens.
It turns out that it can't load a file if folders have spaces in the name.

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 !!

Flutter: Get a PopupMenuButton's PopupMenuItem text in unit tests

I have a PaginatedDataTable that has a DataCell with a PopupMenuButton. WidgetTester can find each DataCell no problem but I cant seem to reference the PopupMenuButton's items to try to select one.
How can i get a PopupMenuButton's PopupMenuItem text in unit tests? Am I using await tester.pump(); correctly to allow the menu to appear?
Here is how im doing it now:
...
expect(find.byIcon(Icons.more_horiz).first, findsOneWidget); // works!
await tester.tap(find.byIcon(Icons.more_horiz).first);
await tester.pump();
var byType = find.text('Quote');
expect(byType, findsOneWidget); // fails!
Which fails with
zero widgets with text "Quote" (ignoring offstage widgets)...
And here is the DataCell markup
...
new DataCell(...),
new DataCell(new PopupMenuButton<quickActions>(
icon: new Icon(Icons.more_horiz),
onSelected: (quickActions action) {
_selectContextAction(action);
},
itemBuilder: (BuildContext context) => <PopupMenuEntry<quickActions>>[
new PopupMenuItem<quickActions>(
value: quickActions.edit,
child: new Text('Edit'),
),
new PopupMenuItem<quickActions>(
value: quickActions.remove,
child: new Text('Remove'),
),
new PopupMenuItem<quickActions>(
value: quickActions.reschedule,
child: new Text('Re-schedule'),
),
new PopupMenuItem<quickActions>(
value: quickActions.bid,
child: new Text('Quote'),
),
],
))
I know this is an old question but I found myself in this same problem and I have solved it.
For those having this same problem, I am posting my answer. My test is a little different than yours.
What is Pump
Triggers a frame after duration amount of time.
This makes the framework act as if the application had janked (missed
frames) for duration amount of time, and then received a v-sync signal
to paint the application.
What is PumpAndSettle
Repeatedly calls pump with the given duration
until there are no longer any frames scheduled. This will call pump at
least once, even if no frames are scheduled when the function is
called, to flush any pending microtasks which may themselves schedule
a frame.
//My Code
//await tester.tap(find.byIcon(Icons.text_fields));
//await tester.pumpAndSettle();
//await tester.tap(find.text(expectedFontSize + ' px'));
//expect(fontSize, expectedFontSize);
//call again if you want to do testing again or something again
//await tester.pumpAndSettle();
var mainButton = find.byIcon(Icons.more_horiz);
expect(mainButton, findsOneWidget);
await tester.tap(mainButton);
await tester.pumpAndSettle();
var childButton = find.text('Quote');
expect(childButton , findsOneWidget); //
For me the code worked only after calling PumpAndSettle Method

How to display an HTML asset file?

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.

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