How can I display Asset Images on Flutter web? - flutter

When I try to display an asset in Flutter Web using the following syntax, no image is shown:
Image.asset(
'assets/image.png',
)
On mobile, the same code will show an image.
The structure is as follows:
assets
image.png
lib
...
pubspec.yaml
In the Pubspec, I declare the asset like this:
flutter:
assets:
- assets/image.png

All assets are stored in an assets directory in Flutter Web, which means that the image asset will be stored like this on web:
assets
assets
image.png
index.html
...
With this knowledge, we can create a widget that handles the behavior for both mobile and web.
Note that we can use Image.network on web because the images are accessed through the network on web by default (that is how web works in general). The path would be yourdomain.com/assets/assets/image.png.
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
class PlatformAwareAssetImage extends StatelessWidget {
const PlatformAwareAssetImage({
Key key,
this.asset,
this.package,
}) : super(key: key);
final String asset;
final String package;
#override
Widget build(BuildContext context) {
if (kIsWeb) {
return Image.network(
'assets/${package == null ? '' : 'packages/$package/'}$asset',
);
}
return Image.asset(
asset,
package: package,
);
}
}
Potentially, this will be changed in future Flutter versions - this is the state of things as of v1.18.0-6.0.pre.82.

For some reason, assets in the web builds are currently placed under another assets/ directory, which results in the path being assets/assets/....
Create a simple function e.g. in lib/utils.dart:
import 'package:flutter/foundation.dart';
String path(str) {
return (kIsWeb) ? 'assets/$str' : str;
}
Wrap any path strings with this function, for example AssetImage(path('images/bg.jpg')).

I find this simpler:
Image.asset("${(kDebugMode && kIsWeb)?"":"assets/"}images/mds_square.png"),
I always need the "assets/" prefix since my "images" folder are in "assets" folder; however, in web debug I need to emit the "assets/" prefix.

Related

Assets is being loaded in cache but not found for AudioPlayer

I'm using the package: audioplayers: ^1.0.1
I'm trying to follow the migration guide of audioplayers found in this link: https://github.com/bluefireteam/audioplayers/blob/main/migration_guide.md
But I can't make it to work.
Here's my pubspec
flutter:
assets:
- assets/sounds/
If I do it like this, it can't load the assets.
final playerSound = AudioPlayer();
await playerSound.setSource(AssetSource('assets/sounds/Pop (1).wav'));
Unhandled Exception: Unable to load asset: assets/assets/sounds/Pop (1).wav
But if I remove the assets, it is trying to find in the cache. I don't get why it is working. Please help to clarify. Thanks!
final playerSound = AudioPlayer();
await playerSound.setSource(AssetSource('sounds/Pop (1).wav'));
java.io.FileNotFoundException: /data/user/0/com.MyName.MyApp/cache/sounds/Pop%20(1).wav: open failed: ENOENT (No such file or directory)
Ok so what i see from the package structure.
If you Go into the AssetSource class which looks as follows :
source.dart file
/// Source representing the path of an application asset in your Flutter
/// "assets" folder.
/// Note that a prefix might be applied by your [AudioPlayer]'s audio cache
/// instance.
class AssetSource extends Source {
final String path;
AssetSource(this.path);
#override
Future<void> setOnPlayer(AudioPlayer player) {
return player.setSourceAsset(path);
}
}
Here if you go inside the setSourceAsset method :
/// Sets the URL to an asset in your Flutter application.
/// The global instance of AudioCache will be used by default.
///
/// The resources will start being fetched or buffered as soon as you call
/// this method.
Future<void> setSourceAsset(String path) async {
final url = await audioCache.load(path);
return _platform.setSourceUrl(playerId, url.path, isLocal: true);
}
if you see the line
final url = await audioCache.load(path);
so this in this AudioCache class if you see the constructor :
/// This is the path inside your assets folder where your files lie.
///
/// For example, Flame uses the prefix 'assets/audio/'
/// (you must include the final slash!).
/// The default prefix (if not provided) is 'assets/'
/// Your files will be found at <prefix><fileName> (so the trailing slash is
/// crucial).
String prefix;
AudioCache({this.prefix = 'assets/'});
So the prefix is assets added as a default in the constructor. So you don't have to call it every time only the file name followed by the folder if you are adding in any case.
After removing the spaces in the name. It is now working. Thanks to #Olek L. and #farouk osama

unable to load Image in local packages

I am developing a local package for my flutter project, it's basically a UI library which includes a lot of image assets.
I don't want to load the assets in the package from my main project.
I want to load the assets in the package itself.
Here's what I did,
asset location: package_name/lib/assets
pubspec.yaml: packages/package_name/assets/bg.png
dart file: packages/package_name/assets/bg.png
Here are the step by step instructions to add image in Flutter:
Step 1: At the root of your project, create a new folder called assets.
Step 2: Inside the root folder, create another folder called images. You can give any name to this folder such as pictures, graphics, etc.
Step 3: Add your images inside the assets/images folder.
Step 4: Provide the image path in pubspec.yaml file as below.
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: <-- SEE HERE
- assets/images/lion.png
Step 5: Display image using the Image.asset() widget
u can use tihs idea
var files = Directory("${Directory.current.path.toString()}/lib/assets").listSync(); // this to create list of all file in the path
#override
void initState() {
// TODO: implement initState
print(files.toList()[0].path);// to see the alemint that u have and u //can creat list of all item and reach to them like this above
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Container(
child: files.isNotEmpty ? Image.file(File('${files.toList() [0].path}'),fit: BoxFit.fill,) : Text(""),
),
),
);
}
do not forget add
import 'dart:io';

Import js.dart and html.dart for a mixed web / mobile flutter project

I'm using Flutter 2.5.2. I have a project which is to be used for both web and mobile (Android / iOS).
There is one particular widget where I need to use one version of the widget when deploying for web, which uses the JS and HTML packages. When deploying for mobile, I need to use a different version which just uses standard Flutter widgets. (The reasons for this are complex - I'm embedding Unity inside flutter).
So for example, I have this web_player.dart version for web:
import 'dart:html' as html;
import 'package:js/js.dart';
import 'package:flutter/material.dart';
#JS('loadPlayer')
external String loadPlayer();
class WebVersion extends StatelessWidget {
const WebVersion({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
// Use the HTML package and return an HtmlElementView
}
}
And this mobile_player.dart version for mobile:
class MobileVersion extends StatelessWidget {
const MobileVersion({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Text("Mobile version")
}
}
Building for web is fine. The problem is that when I build for mobile, the build breaks with:
Error: Not found: 'dart:js'
and
Error: Not found: 'dart:html'
I'm aware that these packages do not exist in the mobile platform, and I'm trying to find a workaround. I initially tried to conditionally use the WebPlayer or MobilePlayer widgets depending on the kIsWeb flag, but this didn't make any difference (I guess because kIsWeb is not a complile-time constant?)
So then I attempted to work around this using conditional imports. So, I created a stub player_stub.dart:
import 'package:flutter/material.dart';
Widget getPlayer() => throw UnsupportedError("This is a stub");
I import this stub in a wrapper Player widget:
import 'package:mypackage/player_stub.dart'
if (dart.library.io) 'package:mypackage/mobile_player.dart'
if (dart.library.js) 'package:mypackage/web_player.dart';
class Player extends StatelessWidget {
const Player ();
#override
Widget build(BuildContext context) {
return getPlayer();
}
}
And then added stub implementations in my mobile_player.dart:
Widget getPlayer() => MobilePlayer();
and in my web_player.dart:
Widget getPlayer() => WebPlayer();
I was hoping this would resolve the build error by compile time tree shaking or something, but it doesn't.
How do I solve this? I'm totally stumped.
Just to answer my own question for others looking for an answer: the solution was moving the stub, the mobile widget and the web widget to a separate dart library.
The library uses conditional exports like this in it's root my_player_package.dart file:
export 'src/player_stub.dart'
if (dart.library.js) 'src/web_player.dart'
if (dart.library.io) 'src/mobile_player.dart';
So now I reference my separate library in my pubspec.yaml:
dependencies:
...
my_player_package:
path: ../my_player_package
And I can now import 'package:my_player_package/my_player_package.dart' and build for both web and mobile.
I'm attempting to use the js package in a cross-platform app and this was my solution.
1. Create a js_stub.dart file
class JS {
final String? name;
const JS([this.name]);
}
allowInterop<F extends Function>(F f){
throw UnimplementedError();
}
2. Use a conditional import statement
import 'package:pwa_install/js_stub.dart' if (dart.library.js) 'package:js/js.dart';
The rest of my code was left untouched and the app runs as expected on web and mobile.

How to access assets in package

The original question comes from flutter issue 32799
I develop a dart package, i need load some json file in runtime, but when I do this occur an error. load image is no problem ,the code:
void main() {
Future<void> loadAsset() async {
String value = await rootBundle
.loadString('lib/src/assets/JsonConfig/test.json');
//Image img = Image.asset('lib/src/assets/default-logo.png');
}
test('adds one to input values', () async {
await loadAsset();
});
}
my pubspec.yaml file like this:
flutter:
# To add assets to your package, add an assets section, like this:
assets:
- lib/src/assets/default-logo.png
- lib/src/assets/JsonConfig/test.json
- lib/src/assets/
To load assets from packages, you should add the prefix 'packages/<package_name>/' for the key to making it works.
Such as how AssetImage do
/// The name used to generate the key to obtain the asset. For local assets
/// this is [assetName], and for assets from packages the [assetName] is
/// prefixed 'packages/<package_name>/'.
String get keyName => package == null ? assetName : 'packages/$package/$assetName';
https://github.com/flutter/flutter/blob/fba99f6cf9a14512e461e3122c8ddfaa25394e89/packages/flutter/lib/src/painting/image_resolution.dart#L146
So add the prefix 'packages/<package_name>/' for the key will work on the demo above:
String value = await rootBundle
.loadString('packages/<package_name>/lib/src/assets/JsonConfig/test.json');

What is the proper code style for relative imports in Dart?

I am new to Flutter and Dart. How should I import files found on the same folder and those from other folders? Is it proper to use ../ or should I write with the package url?
import 'package:flutter/material.dart';
import 'package:my_app/core/widgets/menus.dart' show SidebarMenu;
import './settings.dart' show SettingsScreen;
class DashboardScreen extends StatefulWidget {
DashboardScreen({Key key, this.title}) : super(key: key);
final String title;
static const String routeName = '/dashboard';
#override
_DashboardState createState() => _DashboardState();
}
class _DashboardState extends State<DashboardScreen> {
#override
Widget build(BuildContext ctx) {
return Scaffold(
body: landscape(),
);
}
Widget landscape() {
return new SafeArea(
child: Container(
child: new Row(
children: <Widget>[
new Expanded(
child: new SidebarMenu(),
),
],
),
),
);
}
}
The Dart style guide recommends using relative imports.
It has the advantage of staying correct even if you rename your package (which is admittedly rare). It's also short and makes it very clear to the reader that this is a library in the same package.
There have been issues when libraries inside the lib/ directory of a package are referenced using a file path. Currently, if your entry point is in the lib/ dir, which Flutter uses, the Dart compiler will recognize that and change the entry point URI to a package: URI. That avoids most of the issues that was attempted fixed by using full package: URIs. You should still not refer to a file inside the lib/ directory with a path containing lib/ in any other place (like from the bin/ or test/ directories of the pub package).
If you watch the Flutter Boring Show on youtube (sorry, cannot remember the episode, they mention the existence of a bug where use of the relative path (../) can cause issues when used in combination with the package path.
While this bug may have been resolved, they do mention a few times that the package url is the preferred style.