In Flutter, How to get the width and height of a imageProvider?
In the example below, I need to get the width of the imageProvider. So that I can calculate the minScale for the PhotoView widget in photo_view package.
import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart';
class ImageViewerScreen extends StatelessWidget {
final ImageProvider imageProvider;
ImageViewerScreen({#required this.imageProvider});
#override
Widget build(BuildContext context) {
final double _screenWidth = MediaQuery.of(context).size.width;
final double _imageWidth =
imageProvider.width; //Here is the question: how to get the width of this imageProvider?
final double _minScale = _screenWidth / _imageWidth;
return Scaffold(
appBar: AppBar(),
body: Container(
child: PhotoView(
imageProvider: imageProvider,
minScale: _minScale,
)),
);
}
}
Related
I want to create a rounded image widget, but it ends up pixelated.
With Image.network(url), I get the following:
while the original looks like this:
Here is the relevant code:
class RoundedImage extends StatelessWidget {
final String URL;
final double size;
final bool dynamicallySized;
final double borderRadius;
final bool onlyTopBorderRadius;
const RoundedImage({
#required this.size,
#required this.url,
this.dynamicallySized = false,
this.borderRadius = 8.0,
this.onlyTopBorderRadius = false,
});
#override
Widget build(BuildContext context) {
final newSize = dynamicallySized ? PaddingUtils.getPadding(context, padding: size) : size;
return ClipRRect(
borderRadius:
onlyTopBorderRadius ? BorderRadius.vertical(top: Radius.circular(borderRadius)) : BorderRadius.circular(borderRadius),
child: CachedNetworkImage(
imageUrl: url,
height: newSize,
width: newSize,
fit: BoxFit.cover,
),
);
}
}
Try to add this property to CachedNetworkImage
filterQuality: FilterQuality.high
I want a way to load placeholder image if network/asset image fails to load. here's what i have tried.
class CircularImageWidget extends StatelessWidget {
CircularImageWidget({
Key key,
#required this.radius,
#required this.assetPath,
#required this.imageType,
}) : super(key: key);
final double radius;
final String assetPath;
final ImageType imageType;
Image getErrorImage() {
return Image.asset(AssetUtils.profilepic);
}
ImageProvider returnImageAccordingToImageType() {
if (imageType == ImageType.Network) {
return NetworkImage(
assetPath,
);
} else {
return AssetImage(
assetPath,
);
}
}
#override
Widget build(BuildContext context) {
return CircleAvatar(
backgroundImage: returnImageAccordingToImageType() ??
AssetImage(AssetUtils.profilepic),
radius: radius,
onBackgroundImageError: (exception, stackTrace) {
return AssetImage(AssetUtils.profilepic);
},
);
}
}
You can achieve this by checking the URL of the image and displaying different widgets based on it.
#override
Widget build(BuildContext context) {
return CircleAvatar(
imageURL== null? backgroundImage:
AssetImage(AssetUtils.profilepic),
radius: radius,
):backgroundImage:
NetworkImage(imageURL),
radius: radius,
),
}
I'm trying to rotate a widget (which is not part of the problem as it handles the rotation itself via constructor parameter) based on an animation which interpolates between the previous rotation position a new one obtained with a plugin function. The value of that function (FlutterCompass.events.listen) updates asynchroniously on a regular basis, and it rebuilds the Tween objetc everytime in order to represent the update of the position of the widget. Here's my code:
import 'package:flutter/material.dart';
import 'package:flutter/animation.dart';
import 'package:flutter_compass/flutter_compass.dart';
import 'package:compass_test/compass.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin{
double _direction;
double _angle = 0.0;
Animation<double> _animation;
AnimationController _animationController;
Tween<double> _tween;
#override
void initState() {
super.initState();
_animationController =
AnimationController(duration: Duration(milliseconds: 400), vsync: this);
_tween = Tween<double>(begin: 0.0, end: 0.0);
_animation = _tween.animate(_animationController)
..addListener(() {
setState(() {
_angle =_animationController.value;
});
});
_direction = 0;
FlutterCompass.events.listen((double direction) {
print(_animationController.status);
if(_direction !=direction){
_tween = Tween<double>(
begin: _direction,
end: direction);
_animationController.reset();
_tween.animate(_animationController);
_animationController.forward();
}
_direction = direction;
});
}
#override
void dispose(){
_animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Column(
children: <Widget>[
Center(
child: Container(
margin: EdgeInsets.only(top: 100),
child: Compass(height: 300, width: 300, angleToNorth: _angle)
)
)
],
)
),
);
}
}
However, as I've been able to see with some debug, the values returned from _animationController.value only vary from 0.0 to 1.0, which is not what I tought was supposed to happen: I expected them to vary from the previous value of _direction to the new direction value. How can I achive that?
Thanks in advance
That is how animation controllers are suppose to work. With some extra lines you can even add a curve to the change in values from 0.0 to 1.0. But the values will always go from 0.0 to 1.0. So what you can do is update _angles value like this _angle = degree of angle * _animationController.value. So lets say your angle is 50 degree. So when you start your animation the animation controller's value will start from 0.0 which will be multiplied by 50 giving you 0. And as the animation controllers value moves from 0.0 to 1.0 the _angle's value will also change and in the end giving you 50 as 1.0 * 50 is 50! But as you mentioned you just want to rotate your widget. So instead of this you can use a tween animation builder of type double and update the end value if you want to run the animation again; the animation will continue from the previous value.
I'm trying to create a dynamic animation described in the mockup above. What I need is:
A round shape that will represent an avatar (user pic, for example);
A text centered below it.
Below those (that use half of the screen) is a scrollable PageView.
The animation should be as the following:
Begin: In a Stack both centered at the begin.
Animation: Scaling down and sliding the text (with variable lenght) that must be to the right of the avatar.
End: As the second image in the mockup. Side-to-side while the content below keeps scrolling.
Thought in SliverPersistentHeader combined with CustomMultiChildLayout but the problem is that the text starts centered and ends align to the left and I can animate this dynamically. I was trying to remove the offset of the centered text in the end but it doesn't feel right.
Any help or a sample only with this animation would be appreciate. Thank you.
You will need a Sliver to animate your layout based on the scroll offset. More specifically, SliverPersistentHeader in your situation.
CustomMultiChildLayout is not necessary though, you can achieve the same result using tweens and align/padding/stuff. But you can give it a go if your layout starts to become too complex.
The trick is to use the scroll offset given by SliverPersistentHeader to compute the current progression. Then use that progression to position element between their original and final position.
Here's a raw example:
class TransitionAppBar extends StatelessWidget {
final Widget avatar;
final Widget title;
const TransitionAppBar({this.avatar, this.title, Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return SliverPersistentHeader(
pinned: true,
delegate: _TransitionAppBarDelegate(
avatar: avatar,
title: title,
),
);
}
}
class _TransitionAppBarDelegate extends SliverPersistentHeaderDelegate {
final _avatarTween =
SizeTween(begin: Size(150.0, 150.0), end: Size(50.0, 50.0));
final _avatarMarginTween =
EdgeInsetsTween(begin: EdgeInsets.zero, end: EdgeInsets.only(left: 10.0));
final _avatarAlignTween =
AlignmentTween(begin: Alignment.topCenter, end: Alignment.centerLeft);
final _titleMarginTween = EdgeInsetsTween(
begin: EdgeInsets.only(top: 150.0 + 5.0),
end: EdgeInsets.only(left: 10.0 + 50.0 + 5.0));
final _titleAlignTween =
AlignmentTween(begin: Alignment.center, end: Alignment.centerLeft);
final Widget avatar;
final Widget title;
_TransitionAppBarDelegate({this.avatar, this.title})
: assert(avatar != null),
assert(title != null);
#override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
final progress = shrinkOffset / 200.0;
final avatarSize = _avatarTween.lerp(progress);
final avatarMargin = _avatarMarginTween.lerp(progress);
final avatarAlign = _avatarAlignTween.lerp(progress);
final titleMargin = _titleMarginTween.lerp(progress);
final titleAlign = _titleAlignTween.lerp(progress);
return Stack(
fit: StackFit.expand,
children: <Widget>[
Padding(
padding: avatarMargin,
child: Align(
alignment: avatarAlign,
child: SizedBox.fromSize(size: avatarSize, child: avatar),
),
),
Padding(
padding: titleMargin,
child: Align(
alignment: titleAlign,
child: DefaultTextStyle(
style: Theme.of(context).textTheme.title, child: title),
),
)
],
);
}
#override
double get maxExtent => 200.0;
#override
double get minExtent => 100.0;
#override
bool shouldRebuild(_TransitionAppBarDelegate oldDelegate) {
return avatar != oldDelegate.avatar || title != oldDelegate.title;
}
}
which you can use with a CustomScrollView:
Scaffold(
body: CustomScrollView(
slivers: <Widget>[
TransitionAppBar(
avatar: Material(
color: Colors.blue,
elevation: 3.0,
),
title: Text("Hello World"),
),
SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return ListTile(
title: Text('$index'),
);
}),
)
],
),
);
I'm looking to find a path to generating an image (jpeg or png) from within a flutter application. The image would be composed of circles, lines, text etc.
There does appear to be a means of drawing to the screen using a canvas (https://docs.flutter.io/flutter/dart-ui/Canvas/Canvas.html), however there doesn't appear to be the equivalent for creating an image that could be presented within or sent/used outside the application.
Is there any dart library available for drawing an image? It would seem that it possible given the underlying skia framework. In the Dart-html package there is a CanvasRenderingContext2D.
Edit: Getting something like the following working (as per Richard's suggestions) would be a start:
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:ui';
import 'dart:typed_data';
import 'dart:async';
import 'dart:io';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Image _image;
#override
void initState() {
super.initState();
_image = new Image.network(
'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_120x44dp.png',
);
}
Future<String> get _localPath async {
final directory =
await getApplicationDocumentsDirectory(); //From path_provider package
return directory.path;
}
Future<File> get _localFile async {
final path = await _localPath;
return new File('$path/tempImage.png');
}
Future<File> writeImage(ByteData pngBytes) async {
final file = await _localFile;
// Write the file
file.writeAsBytes(pngBytes.buffer.asUint8List());
return file;
}
_generateImage() {
_generate().then((val) => setState(() {
_image = val;
}));
}
Future<Image> _generate() async {
PictureRecorder recorder = new PictureRecorder();
Canvas c = new Canvas(recorder);
var rect = new Rect.fromLTWH(0.0, 0.0, 100.0, 100.0);
c.clipRect(rect);
final paint = new Paint();
paint.strokeWidth = 2.0;
paint.color = const Color(0xFF333333);
paint.style = PaintingStyle.fill;
final offset = new Offset(50.0, 50.0);
c.drawCircle(offset, 40.0, paint);
var picture = recorder.endRecording();
final pngBytes =
await picture.toImage(100, 100).toByteData(format: ImageByteFormat.png);
//Aim #1. Upade _image with generated image.
var image = Image.memory(pngBytes.buffer.asUint8List());
return image;
//new Image.memory(pngBytes.buffer.asUint8List());
// _image = new Image.network(
// 'https://github.com/flutter/website/blob/master/_includes/code/layout/lakes/images/lake.jpg?raw=true',
// );
//Aim #2. Write image to file system.
//writeImage(pngBytes);
//Make a temporary file (see elsewhere on SO) and writeAsBytes(pngBytes.buffer.asUInt8List())
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_image,
],
),
),
floatingActionButton: new FloatingActionButton(
onPressed: _generateImage,
tooltip: 'Generate',
child: new Icon(Icons.add),
),
);
}
}
PictureRecorder lets you create a Canvas, use the Canvas drawing methods and provides endRecording() returning a Picture. You can draw this Picture to other Scenes or Canvases, or use .toImage(width, height).toByteData(format) to convert it to PNG (or raw - jpeg isn't supported).
For example:
import 'dart:ui';
import 'dart:typed_data';
....
PictureRecorder recorder = new PictureRecorder();
Canvas c = new Canvas(recorder);
c.drawPaint(paint); // etc
Picture p = recorder.endRecording();
ByteData pngBytes =
await p.toImage(100, 100).toByteData(format: ImageByteFormat.png);
Make sure that you are on flutter 0.4.4, otherwise you may not have the format parameter available.
Having seen your edit, though, I suspect you are really looking for CustomPainter where a Widget gives you a Canvas on which you can draw. Here's an example from a similar question.