How do you use SVG's on Flutter Web? - flutter

The flutter_web docos say to that dart:svg is ported, but how do you use that with the flutter asset / widget system?

Honestly the best solution I've found is to load the image over a network for the web. Store the asset on a CDN somewhere, get it from the CDN on the web version, and retrieve it from your assets locally on iOS/Android.
I've created a CrossPlatformSvg widget using this library: https://pub.dev/packages/flutter_svg, something like:
class CrossPlatformSvg {
static Widget asset(
String assetPath, {
double width,
double height,
BoxFit fit = BoxFit.contain,
Color color,
alignment = Alignment.center,
String semanticsLabel,
}) {
// `kIsWeb` is a special Flutter variable that just exists
// Returns true if we're on web, false for mobile
if (kIsWeb) {
return Image.network(
assetPath,
width: width,
height: height,
fit: fit,
color: color,
alignment: alignment,
);
} else {
return SvgPicture.network(
assetPath,
width: width,
height: height,
fit: fit,
color: color,
alignment: alignment,
placeholderBuilder: (_) => Container(
width: 30,
height: 30,
padding: EdgeInsets.all(30),
child: CircularIndicator(),
),
);
}
}
}

#aterenshkov's approach worked for me (see comments under Jack's answer). Here are the details to implement...
iOS / Android
child: SvgPicture.asset('assets/yourimage.svg')
Web
// remove assets folder reference
child: SvgPicture.asset('yourimage.svg')
Combine these 2 together...
import 'package:flutter/foundation.dart'; // provides kIsWeb property
...
child: kIsWeb ? SvgPicture.asset('yourimage.svg') : SvgPicture.asset('assets/yourimage.svg')

My Flutter web app would render SVGs in a desktop web browser but not a mobile web browser using flutter_svg. I found it necessary to build with canvaskit support in order for SVGs to render on mobile:
flutter build web --web-renderer canvaskit
However, the docs state that canvaskit adds about 2MB in download size, which was a dealbreaker for me. Sad to say, but I will be using raster images until this is resolved.

flutter_svg version 1.0.0 is out since Dec 2, 2021 including web support. So there is no longer a blocker to use svg images inside a Flutter project.

#RumbleFish has nicely but using ternary operation at many can make the code messy.
My approach to this is:
Create an Extension Function on String to replace assets/ from the path.
extension ForWeb on String {
String forWeb({required bool web}) => web ? this.replaceFirst('assets/','') : this;
}
Now in code:
SvgPicture.asset("assets/images/logo.png".forWeb(web: kIsWeb));

Related

Cached Network SVG Image

I want to display an SVG picture retrieved from network with the help of Flutter_SVG package and use the same picture in different pages with the Hero widget. But each time I use SvgPicture.network() widget, it loads the image again and again from network;
Page 1 & Page 2
Hero(
tag: 'randomTag',
child: SvgPicture.network(url),
),
I have tried to combine Cached Network Image dependency with Flutter_SVG package but don't know how to use them as a widget together;
Hero(
tag: 'randomTag',
child: CachedNetworkImage(
imageUrl: url,
imageBuilder: (context, ImageProvider<Object> imageProvider) {
// How to use ImageProvider<Object> with SvgPicture?
}
),
),
svg_cached_network_image dependency is not an option since it is not compatible with other dependencies that I use.
Use flutter_cache_manager to save svg from internet
This line get svg from internet and save it to local, and next time you call it it will get from local
var file = await DefaultCacheManager().getSingleFile(url);
https://pub.dev/documentation/flutter_cache_manager/latest/flutter_cache_manager/BaseCacheManager/getSingleFile.html

Exception Flutter from fetch network Image without connection internet

after turning off the internet i gets this error: "Remove network images from cache on any exception during loading"
my app fetch pictures from api. how can i fix this bug how i prevent that?
return Column(children: [
GestureDetector(
child: Image.network(thumbnailUrl))])
You can use the Cached Network Image Plugin if you are developing for android and ios. In Web, the plugin is not working as expected.
Use the following as a widget for your application.
import 'package:cached_network_image/cached_network_image.dart';
Widget commonCacheImageWidget(String? url, {double? width, BoxFit? fit, double? height}) {
if (url!.startsWith('http')) {
return CachedNetworkImage(
placeholder: (context, url) => Image.asset('assets/path....', fit: BoxFit.cover),
imageUrl: url,
height: height,
width: width,
fit: fit,
);
} else {
return Image.asset(url, height: height, width: width, fit: fit);
}
}
In Placeholder you can have any widget, here am using a local image that is stored as an asset.
Or You can check whether the internet connection is there or not before loading the image and change that to some other widget.

Flutter - "build web" results in no gradient, but "run -d chrome" has gradient

The screenshot says it all.
On the left is flutter run -d chrome and the blue gradient, which is a result of BoxDecoration being added to a Container.
On the right is flutter build web followed by firebase deploy --only hosting. The build web command runs with no errors and the deploy command uploads with no errors, but the gradient, as you can see, is gone, resulting in a gray background.
Since flutter does not produce any typical html files or css that I can find it is pretty much impossible to inspect or find why the gradient is not working in the produced web files. Any thoughts on what I am missing with flutter build web considerations or is this a bug that needs to be reported to flutter?
/// In class "TopClipper"
static get gradient => const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [
Color(0xFF333F7A),
Color(0xFF9CBCD9),
],
),
);
/// This ClipPath and call to gradient is in another file
/// that is in a SliverPersistentHeader delegate.
ClipPath(
clipper: TopClipper(),
child: Container(
/// Calling static "gradient" above.
decoration: TopClipper.gradient,
width: double.infinity,
height: expandedHeight,
child: const Spacer(),
),
)
UPDATE and FIX
Thanks to #IvoBeckers comment regarding an unexpected gray background being indicative of an error, I checked the logs, which I should have paid more attention to, even though it did render, minus the gradient.
And sure enough there was an EXCEPTION CAUGHT BY WIDGETS LIBRARY pointing to my incorrect use of Spacer() as the only child of Container(). It was unnecessary as well because the container will grow to it's set width/height anyway.
Removing Spacer() removed all errors and now flutter run -d chrome --release displays the gradient.
ClipPath(
clipper: TopClipper(),
child: Container(
/// Calling static "gradient" above.
decoration: TopClipper.gradient,
width: double.infinity,
height: expandedHeight,
),
)

Flutter cant load image from url

════════ Exception caught by image resource service ════════════════════════════
The following ImageCodecException was thrown resolving an image codec:
Failed to load network image.
Image URL: https://cdn.sportmonks.com/images/soccer/leagues/5.png
Trying to load an image from another domain? Find answers at:
https://flutter.dev/docs/development/platform-integration/web-images
When i try the demo URL https://picsum.photos/250?image=9 it is working but the url above is good so what can be the problem?
class ListRow extends StatelessWidget {
final String name;
final imageUrl;
const ListRow({Key key, this.name, this.imageUrl}) : super(key: key);
#override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
width: 18,
height: 18,
child: Image(
image: NetworkImage(
'https://cdn.sportmonks.com/images/soccer/leagues/5.png'),
),
),
SizedBox(width: 10),
Flexible(
child: new Text(
name,
style:
new TextStyle(fontWeight: FontWeight.bold, color: Colors.black),
),
),
],
);
}
}
So flutter web has upgraded default rendered(HTML) -> CanvasKit and it has better performance.
You are most likely getting this error because CORS(can check chrome network tab to be sure).
To solve it could:
Update cors settings server side: https://stackoverflow.com/a/66104543/4679965
Run app with old rendered:
flutter run -d chrome --web-renderer html // to run the app
flutter build web --web-renderer html --release // to generate a production build
Or display images with platform view:
import 'dart:html';
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
class MyImage extends StatelessWidget {
#override
Widget build(BuildContext context) {
String imageUrl = "image_url";
// https://github.com/flutter/flutter/issues/41563
// ignore: undefined_prefixed_name
ui.platformViewRegistry.registerViewFactory(
imageUrl,
(int _) => ImageElement()..src = imageUrl,
);
return HtmlElementView(
viewType: imageUrl,
);
}
}
I get the same error, for a couple of weeks now, when trying to run an app from the master channel.
I got it working by forcing the web renderer to be HTML:
flutter run -d chrome --no-sound-null-safety --web-renderer=html
When you build your app for web you should:
flutter build web --no-sound-null-safety --web-renderer=html
By default the web renderer is auto, choosing the canvaskit web renderer on desktop browsers and html on mobile.
If you want to avoid this altogether you can stay on the beta or dev channels.
if it is working with another image then it should be a server-side error where the image is stored.
Could be an issue with the codec format. Try running the same code with another image url. If it is working, then the above error could be a technical glitch.
You could try this widget
https://pub.dev/packages/meet_network_image
You can just use this package https://pub.dev/packages/image_network
Using HTML renderer might break other things (like SVG rendering in my case)
Try not to define height, width for container and add fit:BoxFit.cover as a parameter of NetworkImage

How do I make a child widget expand to fill a parent container inside of a stack when the child has no parameters to alter its layout?

I'm building a card game and using flame to pull the cards from a sprite sheet. The problem is that I set the width and height of the Container that holds the SpriteWidget, but the SpriteWidget expands to either the width or the height of the container, but not both. I want it to expand/stretch to be the same size as the parent container. Unfortunately, the SpriteWidget really has no parameters that could be used to change its size.
I've spent several hours scouring the internet for a solution and tried a number of widgets including FittedBox, Flex, Positioned.fill, etc., but I'm unable to achieve the desired effect. How can I make the SpriteWidget stretch to fill its parent when it has no parameters to do so?
class _PlayerHandPortraitLayout
extends WidgetView<PlayerHand, _PlayerHandController> {
#override
final state;
const _PlayerHandPortraitLayout(this.state) : super(state);
#override
Widget build(BuildContext build) {
return Stack(
children: state.displayHand().asMap().entries.map((cardItem) {
var index = cardItem.key;
var card = cardItem.value;
return Positioned(
left: index * CARD_OVERLAP_OFFSET,
child: Draggable<Container>(
childWhenDragging: Container(),
child: Container(
color: Colors.purple,
width: state.cardWidth,
height: state.cardHeight,
child: SpriteWidget(
sprite: state.spriteImages[card.suite.index][card.value.index],
),
),
feedback: Container(
color: Colors.yellow,
width: state.cardWidth,
height: state.cardHeight,
child: SpriteWidget(
sprite: state.spriteImages[card.suite.index][card.value.index],
),
),
),
);
}).toList(),
);
}
}
actually this will be not possible, SpriteWidget is designed to expand as long as it fits on the smallest dimension available on its parent, you can check on it source code here.
This is done so the Sprite will not get distorted when its parent has a different aspect ratio than the ratio of the Sprite.
If you have an use case where you would want the Sprite to get intentionally distorted, please open an issue on the Flame repository explaining the case, and we can try to take a look on it.