i'm using flutter flame library and got this error
The argument type 'Future<Image>' can't be assigned to the parameter
type 'Widget'.
my code is:
import 'package:flutter/material.dart';
import 'package:flame/flame.dart';
class TileMap extends StatefulWidget {
#override
_TileMapState createState() => _TileMapState();
}
class _TileMapState extends State<TileMap> {
#override
Widget build(BuildContext context) {
return Container(
child: Flame.images.load('grass_05.png'),
);
}
}
how to fix it?
and what's the problem?
thank's to all
class _TileMapState extends State<TileMap> {
#override
void initState() {
super.initState();
_loadImage()
}
void _loadImage() async {
var img = await Flame.images.load('grass_05.png');
setState(() => _image = img);
}
Image _image;
#override
Widget build(BuildContext context) {
return Container(
child: _image,
);
}
}
This is how I show a picture:
return Scaffold(
body: Center(
child: Image.asset('man_face.jpg'),
),
);
And this the result: https://imgur.com/a/CPrQgvS
I want to show only special part of the picture. For example a rectangle with x: 250 and y: 360 and width: 200 and height: 150.
Which it should be something like this: https://imgur.com/a/p41y3nx
How can I do that?
you might want to look into this library. brendan-duncan/image. a great tool to manipulate images in flutter.
Here is a code I came up with
it accept an image url and a rect and display only the rect part of the image
import 'dart:async';
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
class PartImagePainter extends StatefulWidget {
String imageUrl;
Rect rect;
PartImagePainter(this.imageUrl, this.rect);
#override
_PartImagePainterState createState() => _PartImagePainterState();
}
class _PartImagePainterState extends State<PartImagePainter> {
Future<ui.Image> getImage(String path) async {
Completer<ImageInfo> completer = Completer();
var img = new NetworkImage(path);
img
.resolve(ImageConfiguration())
.addListener(ImageStreamListener((ImageInfo info, bool _) {
completer.complete(info);
}));
ImageInfo imageInfo = await completer.future;
return imageInfo.image;
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getImage(widget.imageUrl),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If the Future is complete, display the preview.
return paintImage(snapshot.data);
} else {
// Otherwise, display a loading indicator.
return Center(child: CircularProgressIndicator());
}
});
}
paintImage(image) {
return CustomPaint(
painter: ImagePainter(image, widget.rect),
child: SizedBox(
width: MediaQuery.of(context).size.width,
height: widget.rect.height,
),
);
}
}
class ImagePainter extends CustomPainter {
ui.Image resImage;
Rect rectCrop;
ImagePainter(this.resImage, this.rectCrop);
#override
void paint(Canvas canvas, Size size) {
if (resImage == null) {
return;
}
final Rect rect = Offset.zero & size;
final Size imageSize =
Size(resImage.width.toDouble(), resImage.height.toDouble());
FittedSizes sizes = applyBoxFit(BoxFit.fitWidth, imageSize, size);
Rect inputSubRect = rectCrop;
final Rect outputSubRect =
Alignment.center.inscribe(sizes.destination, rect);
final paint = Paint()
..color = Colors.white
..style = PaintingStyle.fill
..strokeWidth = 4;
canvas.drawRect(rect, paint);
canvas.drawImageRect(resImage, inputSubRect, outputSubRect, Paint());
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
try using Alignment property and set fit to none.
Image.network(
"your image.png",
// move on the X axis to right 10% of the image and 0% on the Y Axis
alignment: const Alignment(0.1,0),
// set fit to none
fit: BoxFit.none,
// use scale to zoom out of the image
scale: 2,
)
One way suggested by the official doc of Flutter, is to:
To display a subpart of an image, consider using a CustomPainter and Canvas.drawImageRect.
ref: https://api.flutter.dev/flutter/painting/DecorationImage/alignment.html
Thus here is my full code. Use PartImage to show what you want.
class PartImage extends StatefulWidget {
const PartImage({
Key key,
#required this.imageProvider,
#required this.transform,
}) : assert(imageProvider != null),
super(key: key);
final ImageProvider imageProvider;
final Matrix4 transform;
#override
_PartImageState createState() => _PartImageState();
}
class _PartImageState extends State<PartImage> {
ImageStream _imageStream;
ImageInfo _imageInfo;
#override
void didChangeDependencies() {
super.didChangeDependencies();
_getImage();
}
#override
void didUpdateWidget(PartImage oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.imageProvider != oldWidget.imageProvider) _getImage();
}
void _getImage() {
final oldImageStream = _imageStream;
_imageStream = widget.imageProvider.resolve(createLocalImageConfiguration(context));
if (_imageStream.key != oldImageStream?.key) {
final listener = ImageStreamListener(_updateImage);
oldImageStream?.removeListener(listener);
_imageStream.addListener(listener);
}
}
void _updateImage(ImageInfo imageInfo, bool synchronousCall) {
setState(() {
_imageInfo = imageInfo;
});
}
#override
void dispose() {
_imageStream.removeListener(ImageStreamListener(_updateImage));
super.dispose();
}
#override
Widget build(BuildContext context) {
return RawPartImage(
image: _imageInfo?.image, // this is a dart:ui Image object
scale: _imageInfo?.scale ?? 1.0,
transform: widget.transform,
);
}
}
/// ref: [RawImage]
class RawPartImage extends StatelessWidget {
final ui.Image image;
final double scale;
final Matrix4 transform;
const RawPartImage({Key key, this.image, this.scale, this.transform}) : super(key: key);
#override
Widget build(BuildContext context) {
return CustomPaint(
painter: _RawPartImagePainter(
image: image,
scale: scale,
transform: transform,
),
);
}
}
class _RawPartImagePainter extends CustomPainter {
final ui.Image image;
final double scale;
final Matrix4 transform;
final painter = Paint();
_RawPartImagePainter({this.image, this.scale, this.transform});
#override
void paint(Canvas canvas, Size size) {
if (image == null) {
return;
}
final transformInv = Matrix4.inverted(transform);
final dst = Offset.zero & size;
final src = Rect.fromPoints(
transformOffset(transformInv, dst.topLeft),
transformOffset(transformInv, dst.bottomRight),
);
// print('src=$src dst=$dst');
canvas.drawImageRect(image, src, dst, painter);
}
#override
bool shouldRepaint(covariant _RawPartImagePainter oldDelegate) {
return oldDelegate.image != image || //
oldDelegate.scale != scale ||
oldDelegate.transform != transform;
}
}
Offset transformOffset(Matrix4 transform, Offset offset) {
Vector4 vecOut = transform * Vector4(offset.dx, offset.dy, 0, 1);
return Offset(vecOut.x, vecOut.y);
}
By the way, if you are interested in knowing what happens behind drawImageRect:
Have a search https://github.com/flutter/engine/search?q=drawImageRect
This seems the C++ code that drawImageRect (i.e. _drawImageRect actually calls: https://github.com/flutter/engine/blob/6bc70e4a114ff4c01b60c77bae754bace5683f6d/lib/ui/painting/canvas.cc#L330
It calls canvas_->drawImageRect. What is canvas_? From the header file we see it is of type SkCanvas* canvas_;.
Then we go into the world of Skia (not Flutter or Dart anymore). https://skia.org/user/api/skcanvas_overview for an overview of SkCanvas. And https://api.skia.org/classSkCanvas.html#a680ab85c3c7b5eab23b853b97f914334 for the actual SkCanvas.drawImageRect documentation.
How to load a huge bitmap in flutter? For example I have an image 3000x3000px that I need to display in Image widget with size 100x100px. In android SDK I can use BitmapOption.inSampleSize to load downscaled image. Is there have an analogue in Flutter SDK?
as an option you can use ResizeImage widget:
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: Home()));
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
ResizeImage img;
#override
void initState() {
super.initState();
img = ResizeImage(NetworkImage('https://picsum.photos/3000/3000'), width: 100, height: 100);
}
#override
Widget build(BuildContext context) {
return Scaffold(body: Center(child: Image(image: img)));
}
}
if you add this code to initState
img.resolve(ImageConfiguration()).addListener(ImageStreamListener((info, bool _)async {
var data = await info.image.toByteData();
print('==== ${img.width} ${img.height} ${data.buffer.asUint32List().length}');
}));
you will see in log something line this
==== 100 100 10000
I am trying to get an local asset image loaded into an ui.Image object. But the ui.Image is an abstract class. I basically have this :
import 'dart:ui' as ui;
class MyImage implements ui.Image{
int height;
int width;
MyImage(String file){
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
}
#override
String toString() {
// TODO: implement toString
return super.toString();
}
}
Using your code with the function definition Below. The error is at
' ui.Image image = await loadImage(img); ' await is underlined red and tool tip is 'Undefined name 'await' in function body not marked with async.'
class TrialApp extends StatefulWidget {
#override
_TrialAppState createState() => new _TrialAppState();
}
class _TrialAppState extends State<TrialApp> {
NodeWithSize rootNode;
#override
void initState() {
// TODO: implement initState
super.initState();
rootNode = new NodeWithSize(new Size(400.0, 400.0));
}
#override
Widget build(BuildContext context) {
// define a function that converts the I.Image object into ui.Image
//object
Future<ui.Image> loadImage(I.Image img) async {
final Completer<ui.Image> imageCompleter = new Completer();
ui.decodeImageFromList(img.getBytes(), (ui.Image img) {
imageCompleter.complete(img);
});
return imageCompleter.future;
}
// Obtain a `I.Image` object from the image file
I.Image img = I.decodeImage(new io.File('images/tile.png').readAsBytesSync());
// Obtain the `ui.Image` from the `I.Image` object
ui.Image image = await loadImage(img);
Sprite myButton = new Sprite.fromImage(image);
rootNode.addChild(myButton);
return new SpriteWidget(rootNode);
}
}
First obtain the image from the assetbundle using rootBundle. The convert the obtained ByteData to List<int>. Now you can obtain a ui.Image using the decodeImageFromList method.
Example:
// import statements
import 'dart:async';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:spritewidget/spritewidget.dart';
class TrialApp extends StatefulWidget {
#override
_TrialAppState createState() => new _TrialAppState();
}
class _TrialAppState extends State<TrialApp> {
NodeWithSize rootNode = new NodeWithSize(new Size(400.0, 400.0));
#override
void initState() {
super.initState();
init();
}
Future<Null> init() async {
rootNode = new NodeWithSize(new Size(400.0, 400.0));
// Read file from assetbundle
final ByteData data = await rootBundle.load('images/tile.png');
// Convert the obtained ByteData into ui.Image
final ui.Image image = await loadImage(new Uint8List.view(data.buffer)); // Uint8List converts the ByteData into List<int>
Sprite myButton = new Sprite.fromImage(image);
rootNode.addChild(myButton);
// notify to redraw with child
setState(() {
rootNode = rootNode;
});
}
// define a function that converts the List<int> into ui.Image object
Future<ui.Image> loadImage(List<int> img) async {
final Completer<ui.Image> imageCompleter = new Completer();
ui.decodeImageFromList(img, (ui.Image img) {
imageCompleter.complete(img);
});
return imageCompleter.future;
}
#override
Widget build(BuildContext context) {
return new Container(
color: Colors.white,
child: new SpriteWidget(rootNode),
);
}
}
Hope that helps!
If you are using SpriteWidget you can load images using the ImageMap class to save some code. From the SpriteWidget docuementation:
ImageMap images = new ImageMap(rootBundle);
// Load a single image
ui.Image image = await images.loadImage('assets/my_image.png');
// Load multiple images
await images.load(<String>[
'assets/image_0.png',
'assets/image_1.png',
'assets/image_2.png',
]);
// Access a loaded image from the ImageMap
image = images['assets/image_0.png'];
I've been facing some problems related to the setState function while using Stateful Widgets that updates itself with the help of Timers. The code below show 2 main classes that replicate how I came to find this error. The Text Widget "Lorem" should be inserted within 10 seconds - and it is - but it's never shown. I tried to debug the array "Items" and it does contain the "lorem" Text Widget after 5 seconds, as it should. The "build" function runs but doesn't make any difference in the UI.
class textList extends StatefulWidget {
#override
State<StatefulWidget> createState() =>
new _textListState();
}
class _textListState extends State<textList>
with TickerProviderStateMixin {
List<Widget> items = new List();
Widget lorem = new textClass("Lorem");
Timer timer;
#override
void initState() {
super.initState();
items.add(new textClass("test"));
items.add(new textClass("test"));
timer = new Timer.periodic(new Duration(seconds: 5), (Timer timer) {
setState(() {
items.removeAt(0);
items.add(lorem);
});
});
}
#override
void dispose() {
super.dispose();
timer.cancel();
}
#override
Widget build(BuildContext context) {
Iterable<Widget> content = ListTile.divideTiles(
context: context, tiles: items).toList();
return new Column(
children: content,
);
}
}
class textClass extends StatefulWidget {
textClass(this.word);
final String word;
#override
State<StatefulWidget> createState() =>
new _textClass(word);
}
class _textClass extends State<textClass>
with TickerProviderStateMixin {
_textClass(this.word);
String word;
Timer timer;
#override
void initState() {
super.initState();
timer = new Timer.periodic(new Duration(seconds: 2), (Timer timer) {
setState(() {
word += "t";
});
});
}
#override
void dispose() {
super.dispose();
timer.cancel();
}
#override
Widget build(BuildContext context) {
return new Text(word);
}
}
This is not how I came to find this error but this is the simplest way to replicate it. The main idea is: The children texts should keep updating themselves (in this case, adding "t"s in the end) and, after 5 seconds, the last of them should be replaced for the Text Widget "Lorem", what does happen in the list but not in the UI.
Here's what's wrong:
A State should never have any constructor arguments. Use the widget property to get access to final properties of the associated StatefulWidget.
Flutter is reusing your _textClass instance because the class name and keys match. This is a problem since you only set widget.word in initState so you're not picking up the new word configuration information. You can fix this either by giving the StatefulWidget instances unique keys to disambiguate them and cause the old State to be disposed, or you can keep around the old State and implement didUpdateWidget. The latter approach is shown below.
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
home: new Scaffold(
appBar: new AppBar(title: new Text('Example App')),
body: new textList(),
),
));
}
class textList extends StatefulWidget {
#override
State<StatefulWidget> createState() =>
new _textListState();
}
class _textListState extends State<textList>
with TickerProviderStateMixin {
List<Widget> items = new List();
Widget lorem = new textClass("Lorem");
Timer timer;
#override
void initState() {
super.initState();
items.add(new textClass("test"));
items.add(new textClass("test"));
timer = new Timer.periodic(new Duration(seconds: 5), (Timer timer) {
setState(() {
items.removeAt(0);
items.add(lorem);
});
});
}
#override
void dispose() {
super.dispose();
timer.cancel();
}
#override
Widget build(BuildContext context) {
Iterable<Widget> content = ListTile.divideTiles(
context: context, tiles: items).toList();
return new Column(
children: content,
);
}
}
class textClass extends StatefulWidget {
textClass(this.word);
final String word;
#override
State<StatefulWidget> createState() =>
new _textClass();
}
class _textClass extends State<textClass>
with TickerProviderStateMixin {
_textClass();
String word;
Timer timer;
#override
void didUpdateWidget(textClass oldWidget) {
if (oldWidget.word != widget.word) {
word = widget.word;
}
super.didUpdateWidget(oldWidget);
}
#override
void initState() {
super.initState();
word = widget.word;
timer = new Timer.periodic(new Duration(seconds: 2), (Timer timer) {
setState(() {
word += "t";
});
});
}
#override
void dispose() {
super.dispose();
timer.cancel();
}
#override
Widget build(BuildContext context) {
return new Text(word);
}
}