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
Related
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],
),
),
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.
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.
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.
With the Interactive viewer documentation i came to know that we can autoscroll to a particular position in Interactive viewer with toScene method.But i tried but everything in vain.How to autoscroll to an offset given the positions in interactive viewer
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
class MyPlanet extends StatefulWidget {
#override
_MyPlanetState createState() => _MyPlanetState();
}
class _MyPlanetState extends State<MyPlanet> {
final TransformationController transformationController =
TransformationController();
#override
void initState() {
SchedulerBinding.instance.addPostFrameCallback((_) async {
Timer(Duration(seconds: 5), () {
setState(() {
transformationController.value = Matrix4.identity()
..translate(800, 0.0);
transformationController.toScene(Offset(800, 0.0));
});
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return InteractiveViewer(
minScale: 0.5,
maxScale: 1,
constrained: false,
transformationController: transformationController,
child: Container(
height: 896,
width: 2000,
child: Image.asset(
'assets/images/rain_forest.png',
fit: BoxFit.cover,
height: double.infinity,
),
),
);
}
}
Thanks #pskink.
Autoscroll in interactive viewer can be achieved by using TransformationController and matrix4
#override
void initState() {
TransformationController transformationController =
TransformationController();
transformationController.value = Matrix4.identity()
..translate(-200.0, 0.0); // translate(x,y);
}
Above solution not work for me - it move whole image on the screen, not only set needed position. I resolve issue with this example https://api.flutter.dev/flutter/widgets/InteractiveViewer/transformationController.html
Define end point for scroll for example by link:
Matrix4 scrollEnd = Matrix4.identity();
scrollEnd.setEntry(1, 3, -700); // y = 700
_controllerReset.reset();
_animationReset = Matrix4Tween(
begin: Matrix4.identity(),
end: scrollEnd,
).animate(_controllerReset);
_animationReset!.addListener(_onAnimateReset);
_controllerReset.forward();