Display animated gif in flutter, but don't animate - flutter

I have an animated gif that I am displaying in Flutter. I now have the requirement to display that gif in another place but not animate it. Is there a way I can do this, without having to include a duplicate non-aniamted graphic?
Thanks!

You could use flutter_gifimage package:
GifController controller = GifController(vsync: this);
...
GifImage(controller: controller,
image: AssetImage("images/animate.gif"))
...
controller.stop();

So, finally I wrote code that shows animated GIFs without animation.
Use:
StillGIF.asset(
e,
height: 100.0,
width: 100.0,
)
Implementation:
class StillGIF extends StatefulWidget {
final ImageProvider image;
final double width;
final double height;
StillGIF({
Key? key,
required this.image,
required this.width,
required this.height,
}) : super(key: key);
factory StillGIF.asset(
String image, {
Key? key,
required double width,
required double height,
}) =>
StillGIF(
key: key,
image: AssetImage(image),
width: width,
height: height,
);
factory StillGIF.file(
String image, {
Key? key,
required double width,
required double height,
}) =>
StillGIF(
key: key,
image: FileImage(File(image)),
width: width,
height: height,
);
factory StillGIF.network(
String image, {
Key? key,
required double width,
required double height,
}) =>
StillGIF(
key: key,
image: NetworkImage(image),
width: width,
height: height,
);
#override
State<StillGIF> createState() => _StillGIFState();
}
class _StillGIFState extends State<StillGIF> {
RawImage? image;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
Uint8List? data;
if (widget.image is NetworkImage) {
final resolved = Uri.base.resolve((widget.image as NetworkImage).url);
final request = await HttpClient().getUrl(resolved);
final HttpClientResponse response = await request.close();
data = await consolidateHttpClientResponseBytes(response);
} else if (widget.image is AssetImage) {
final key =
await (widget.image as AssetImage).obtainKey(ImageConfiguration());
data = (await key.bundle.load(key.name)).buffer.asUint8List();
} else if (widget.image is FileImage) {
data = await (widget.image as FileImage).file.readAsBytes();
}
final codec =
await PaintingBinding.instance.instantiateImageCodecFromBuffer(
await ImmutableBuffer.fromUint8List(
data!.buffer.asUint8List(),
),
);
FrameInfo frame = await codec.getNextFrame();
setState(() {
image = RawImage(
image: frame.image,
height: widget.height,
width: widget.width,
fit: BoxFit.cover,
);
});
});
}
#override
Widget build(BuildContext context) {
return image ??
SizedBox(
width: widget.width,
height: widget.height,
);
}
}
You can configure other options like BoxFit in RawImage constructor.

Related

How do I show thumbnail of an online pdf

I'm makeing a firebase server based ebook reader in flutter. but can't find any way to show thumbnail of the books.
I tried to use the thumbnailer and pdf_thumbnail package but can't find any way there. I was expecting to download and cache the first page as thumbnail.
Use the https://pub.dev/packages/flutter_pdfview package to render the first page of the PDF as an image, and then display that image as the book thumbnail.
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_pdfview/flutter_pdfview.dart';
class PdfThumbnail extends StatefulWidget {
final String path;
final double height;
final double width;
PdfThumbnail({required this.path, required this.height, required this.width});
#override
_PdfThumbnailState createState() => _PdfThumbnailState();
}
class _PdfThumbnailState extends State<PdfThumbnail> {
late PDFViewController pdfViewController;
Uint8List? imageBytes;
#override
void initState() {
super.initState();
_renderPdf();
}
void _renderPdf() async {
final data = await pdfViewController.render(
page: 1,
x: 0.0,
y: 0.0,
width: widget.width,
height: widget.height,
);
setState(() {
imageBytes = data!;
});
}
#override
Widget build(BuildContext context) {
return imageBytes != null
? Image.memory(
imageBytes!,
width: widget.width,
height: widget.height,
fit: BoxFit.cover,
)
: Container();
}
}
then you can use as
PdfThumbnail(path: 'Insert_your_path_to_pdf', height: 50, width: 50)
I think this will help resolve your problem

Remove White Background While Canvas Is Being Drawn or Redrawn

I use the code below to draw part of the image. Every time I change the image. The widget turns white for a few milliseconds. Any idea how to make the widget transparent while the canvas is being drawn?
//----------------------------------------------------------------------------------
PartImagePainter(
originalImageWidth: imageWidth, originalImageHeight: imageHeight,
imageData: imageData,
rect: imageRects[index]
)
//----------------------------------------------------------------------------------
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
class PartImagePainter extends StatefulWidget {
Uint8List imageData;
int originalImageWidth;
int originalImageHeight;
Rect rect;
PartImagePainter({required this.imageData, required this.originalImageWidth, required this.originalImageHeight, required this.rect});
#override
_PartImagePainterState createState() => _PartImagePainterState();
}
class _PartImagePainterState extends State<PartImagePainter> {
Future<ui.Image> getImage(Uint8List imageData) async {
final codec = await ui.instantiateImageCodec(
imageData,
targetWidth: widget.originalImageWidth,
targetHeight: widget.originalImageHeight,
);
final image = (await codec.getNextFrame()).image;
return image;
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getImage(widget.imageData),
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 SizedBox(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);
canvas.drawImageRect(resImage, inputSubRect, outputSubRect, Paint());
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
You can try use a Stack with a Positioned.fill to make the background transparent. Example:
Stack(
children: [
Positioned.fill(
child: Container(
color: Colors.transparent,
),
),
PartImagePainter(
originalImageWidth: imageWidth,
originalImageHeight: imageHeight,
imageData: imageData,
rect: imageRects[index],
),
],
)
You can also use a Container with a transparent color. Example:
Container(
color: Colors.transparent,
child: PartImagePainter(
originalImageWidth: imageWidth,
originalImageHeight: imageHeight,
imageData: imageData,
rect: imageRects[index],
),
),

How to dynamically expand a widget by/relative to child dimensions with animation flutter

I am trying to expand/animate a container relative to the contained widget dynamic dimensions.
I tried getting the extracting inner widget dimensions by getting its renderbox dimensions.
i have this for getting dimensions change
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
typedef void OnWidgetSizeChange(Size size);
class MeasureSizeRenderObject extends RenderProxyBox {
Size? oldSize;
final OnWidgetSizeChange onChange;
MeasureSizeRenderObject(this.onChange);
#override
void performLayout() {
super.performLayout();
Size newSize = child!.size;
if (oldSize == newSize) return;
oldSize = newSize;
WidgetsBinding.instance.addPostFrameCallback((_) {
onChange(newSize);
});
}
}
class MeasureSize extends SingleChildRenderObjectWidget {
final OnWidgetSizeChange onChange;
const MeasureSize({
Key? key,
required this.onChange,
required Widget child,
}) : super(key: key, child: child);
#override
RenderObject createRenderObject(BuildContext context) {
print('=============bro we here=======');
return MeasureSizeRenderObject(onChange);
}
}
using it like this
...
double _width = 0;
double _height = 0;
...
AnimatedContainerApp(
width: _width,
height: _height,
child: MeasureSize(
onChange: (size) {
print(size.height);
setState(() {
_height = size.height;
});
},
child: !isExpanded
? Container(
height: 200,
width: double.Infinity
)
: Container(
height: 400,
width: double.Infinity
)),
),
with AnimatedContainerApp being a simple AnimatedContainer widget.
I am unable to trigger and update dimensions for the animation. also if the is a better way to achive that I am open for it.
You can use AnimatedSize widget https://api.flutter.dev/flutter/widgets/AnimatedSize-class.html but make sure that it will animate only one side. or else you can check https://stackoverflow.com/a/59133346/19165706 solution here.

How to Draw multiple Rectangle with different rotation in Flutter Canvas?

I am trying to add multiple Rectangles in the Canvas and rotate them with user pan action. But the Constructor I found till now for Rect is all to draw them without Rotation. and I found a method canvas.rotate() which will rotate the whole canvas.
How to achieve this? Any code where rotation of the Rectangle is dealt with user pan action without using canvas.rotate() will be helpful.
The solution is simple as #pskink answered in the comment above.
There is only canvas.rotate() and canvas.transform() to rotate anything in the flutter canvas and there is canvas.scale() to scale them.
now if you want to rotate one object 120, and another 40 degrees you need to draw them inside a canvas.save() ... canvas.restore() block. then your objects will be rotated at a different angles. look at the below code for example:
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:ui' as ui;
const kCanvasSize = 300.0;
class ImageInsideRectPage extends StatefulWidget {
const ImageInsideRectPage({Key? key}) : super(key: key);
#override
_ImageInsideRectPageState createState() => _ImageInsideRectPageState();
}
class _ImageInsideRectPageState extends State<ImageInsideRectPage> {
ui.Image? image;
#override
void initState() {
_load('assets/img.png');
super.initState();
}
void _load(String path) async {
var bytes = await rootBundle.load(path);
image = await decodeImageFromList(bytes.buffer.asUint8List());
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.greenAccent, width: 2)),
height: kCanvasSize,
width: kCanvasSize,
child: CustomPaint(
painter: ImageInsideRectangle(context: context, image: image),
child: SizedBox.expand(),
),
),
),
);
}
}
class ImageInsideRectangle extends CustomPainter {
ImageInsideRectangle({required this.context, required this.image});
ui.Image? image;
final BuildContext context;
#override
void paint(Canvas canvas, Size size) async {
canvas.clipRRect(ui.RRect.fromRectXY(
Rect.fromPoints(Offset(0, 0), Offset(kCanvasSize - 4, kCanvasSize - 4)),
0,
0,
));
Paint greenBrush = Paint()..color = Colors.greenAccent;
if (image != null) {
canvas.save();
rotate(
canvas: canvas,
cx: image!.width.toDouble() / 2,
cy: image!.height.toDouble() / 2,
angle: -0.3);
canvas.scale(kCanvasSize / image!.height);
canvas.drawImage(image!, Offset(0, 0), greenBrush);
canvas.restore();
}
canvas.save();
rotate(canvas: canvas, cx: 200 + 50, cy: 100 + 50, angle: 0.5);
canvas.drawRect(Rect.fromLTWH(200, 100, 100, 100), greenBrush);
canvas.restore();
}
void rotate(
{required Canvas canvas,
required double cx,
required double cy,
required double angle}) {
canvas.translate(cx, cy);
canvas.rotate(angle);
canvas.translate(-cx, -cy);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
Future<ui.Image> loadUiImage(String imageAssetPath) async {
final ByteData data = await rootBundle.load(imageAssetPath);
final Completer<ui.Image> completer = Completer();
ui.decodeImageFromList(Uint8List.view(data.buffer), (ui.Image img) {
return completer.complete(img);
});
return completer.future;
}
This way you can rotate multiple objects in multiple directions. also, there is an example of loading an image from local asset and rotating it around its own center.

How to draw Flutter Widgets off-screen

I'm looking for a solution to draw a Flutter Widget tree in a separate graphic pipeline that will never be displayed on the screen. I want to be able to define the width/height constraints to any size regardless of the display.
The goal is to get a png image of this Widget that is not dependent on the device, to send it to a printer. I know it's possible to use RepaintBundary to build an image but that's only for something displayed on the screen.
I tested something like this, with no luck:
Future<void> capture(BuildContext context, Widget widget) async {
final rect = Rect.fromLTWH(0, 0, 200, 200);
final root = OffscreenPainter(
size: Size(200, 200),
child: ConstrainedBox(
constraints: BoxConstraints.tightFor(width: 200, height: 200),
child: widget,
));
final ro = root.createRenderObject(context);
final el = root.createElement();
// el.activate();
el.attachRenderObject(ro);
final o = PipelineOwner();
// ro.attach(o);
o.rootNode = ro;
ro.scheduleInitialLayout();
final rootLayer = OffsetLayer();
rootLayer.attach(o);
ro.scheduleInitialPaint(rootLayer);
el.updateChildren();
ro.layout(BoxConstraints(maxWidth: rect.width, maxHeight: rect.height));
o.flushLayout();
o.flushCompositingBits();
o.flushPaint();
final im = await ro.toImage();
final bd = await im.toByteData(format: ImageByteFormat.png);
final f = File('image.png');
f.writeAsBytesSync(bd.buffer.asUint8List());
print('saved to ${f.absolute}');
}
class OffscreenPainter extends SingleChildRenderObjectWidget {
/// Creates a widget that isolates repaints.
const OffscreenPainter({Key key, Widget child, #required this.size})
: super(key: key, child: child);
final Size size;
#override
RenderOffscreenPainter createRenderObject(BuildContext context) =>
RenderOffscreenPainter(size);
}
class RenderOffscreenPainter extends RenderRepaintBoundary {
RenderOffscreenPainter(this._size);
final Size _size;
#override
void performLayout() {
if (child != null) {
child.layout(BoxConstraints.tight(_size));
size = child.size;
} else {
size = _size;
}
}
}
You can use the Transform Widget to push your widget off-screen. According to the docs, Transform.translate moves the widget before it's painted.
In this example, I added an Icon to the bottom navigation bar and shifted it down. It doesn't show but I can still access the RepaintBoundary.
Transform.translate(
offset: Offset(0,200),
child: RepaintBoundary(
key: iconKey,
child: IconButton(icon: Icon(Icons.star),
onPressed: () {
// Do something
}),
),
),