I want to clip an image that I extracted from the image picker plugin and it does not work with BoxDecoration.circle , so I want to clip it as circle with oval clipper. How to achive it ?
You can use CircleAvatar widget to display the obtained image to make it circular.
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
void main() => runApp(new MaterialApp(debugShowCheckedModeBanner: false, home: new MyApp()));
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
File _image;
Future getImage() async {
var image = await ImagePicker.pickImage(source: ImageSource.camera);
setState(() {
_image = image;
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Home'),
),
body: new Center(
child: _image == null
? new Text('No image selected.')
: new CircleAvatar(backgroundImage: new FileImage(_image), radius: 200.0,),
),
floatingActionButton: new FloatingActionButton(
onPressed: getImage,
tooltip: 'Pick Image',
child: new Icon(Icons.add_a_photo),
),
);
}
}
You can also use ClipOval to circle the image. Just wrap your file image with ClipOval.
ClipOval(
child: FileImage(_image)
),
If you want to make use of BoxDecoration.Circle, this is what you need to do
Container(
width: 120.0,
height: 120.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
fit: BoxFit.fill,
image: FileImage(_image)
)
)
),
I hope this helps
I have figured it out, this a class that I will use later for clipping it's child
class CircleRevealClipper extends CustomClipper<Rect> { CircleRevealClipper();
#override Rect getClip(Size size) {
final epicenter = new Offset(size.width, size.height);
// Calculate distance from epicenter to the top left corner to make sure clip the image into circle.
final distanceToCorner = epicenter.dy;
final radius = distanceToCorner;
final diameter = radius;
return new Rect.fromLTWH(
epicenter.dx - radius, epicenter.dy - radius, diameter, diameter); }
#override bool shouldReclip(CustomClipper<Rect> oldClipper) {
return true; } }
Related
I really interested with flutter, and currently I'm trying to create a custom clipper with clipPart widget. but it's seems not working correctly.
here is my code
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(
title: "My App",
home: HomePage(),
debugShowCheckedModeBanner: false,
));
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Container(
alignment: Alignment.topCenter,
color: Colors.teal,
child: HomeScreenTop(),
)
],
),
);
}
}
class HomeScreenTop extends StatefulWidget {
#override
_HomeScreenTopState createState() => _HomeScreenTopState();
}
class _HomeScreenTopState extends State<HomeScreenTop> {
#override
Widget build(BuildContext context) {
return Stack(
textDirection: TextDirection.ltr,
children: <Widget>[
Align(
alignment: Alignment.topCenter,
child: WaveContainer(),
),
],
);
}
}
class WaveContainer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ClipPath(
clipper: CustomShapeClipper(),
clipBehavior: Clip.antiAlias,
child: Container(
width: MediaQuery.of(context).size.width,
height: 400.0,
color: Colors.orange,
),
);
}
}
class CustomShapeClipper extends CustomClipper<Path> {
#override
getClip(Size size) {
print(size.height);
final Path path =Path();
path.lineTo(size.width / 2, size.height);
path.lineTo(size.width, 0);
path.close();
return path;
}
#override
bool shouldReclip(CustomClipper oldClipper) => true;
}
what I want to do is, create a triangle from top to bottom, as I know from the tutorial, Offset of x = 0 and y = 0 should be pointing to left-top corner of the page. So if create like this
path.lineTo(size.width / 2, size.height);
path.lineTo(size.width, 0);
the path should be drawing from left-top to middle-down to right-top and back to left-top
But what I've got when I try the code is like this
And, the stack is not working.. the triangle should isolated inside the container but why the custom clipper can exceed the bottom container??
I really confuse, Please help
Solved by change the emulator from Genymotion to iOS IphoneX emulator. Don't know why it show different behaviour between iOS and android, but I think iOS emulator is the right one..
I am using RepaintBoundary to take the screenshot of the current widget which is a listView. But it only captures the content which is visible on the screen at the time.
RepaintBoundary(
key: src,
child: ListView(padding: EdgeInsets.only(left: 10.0),
scrollDirection: Axis.horizontal,
children: <Widget>[
Align(
alignment: Alignment(-0.8, -0.2),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: listLabel(orientation),
)
),
Padding(padding: EdgeInsets.all(5.0)),
Align(
alignment: FractionalOffset(0.3, 0.5),
child: Container(
height: orientation == Orientation.portrait? 430.0: 430.0*0.7,
decoration: BoxDecoration(
border: Border(left: BorderSide(color: Colors.black))
),
//width: 300.0,
child:
Wrap(
direction: Axis.vertical,
//runSpacing: 10.0,
children: colWidget(orientation),
)
)
),
Padding(padding: EdgeInsets.all(5.0)),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: listLabel(orientation),
)
],
),
);
screenshot function:
Future screenshot() async {
RenderRepaintBoundary boundary = src.currentContext.findRenderObject();
ui.Image image = await boundary.toImage();
ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
Uint8List pngBytes = byteData.buffer.asUint8List();
print(pngBytes);
final directory = (await getExternalStorageDirectory()).path;
File imgFile =new File('$directory/layout2.pdf');
imgFile.writeAsBytes(pngBytes);
}
Is there any way, so that I can capture the whole listView, i.e., not only the content which is not visible on the screen but the scrollable content also. Or maybe if the whole widget is too large to fit in a picture, it can be captured in multiple images.
I achieve the solution of this problem using this package: Screenshot, that takes a screenshot of the entire widget. It's easy and simple, follow the steps on the PubDev or GitHub and you can make it work.
OBS: To take a full screenshot of the widget make sure that your widget is fully scrollable, and not just a part of it.
(In my case, i had a ListView inside a Container, and the package doesn't take the screenshot of all ListView because i have many itens on it, SO i have wrap my Container inside a SingleChildScrollView and add the NeverScrollableScrollPhysics physics in the ListView and it works! :D).
Screenshot of my screen
More details in this issue
This made me curious whether it was possible so I made a quick mock-up that shows it does work. But please be aware that by doing this you're essentially intentionally breaking the things flutter does to optimize, so you really shouldn't use it beyond where you absolutely have to.
Anyways, here's the code:
import 'dart:math';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(MyApp());
class UiImagePainter extends CustomPainter {
final ui.Image image;
UiImagePainter(this.image);
#override
void paint(ui.Canvas canvas, ui.Size size) {
// simple aspect fit for the image
var hr = size.height / image.height;
var wr = size.width / image.width;
double ratio;
double translateX;
double translateY;
if (hr < wr) {
ratio = hr;
translateX = (size.width - (ratio * image.width)) / 2;
translateY = 0.0;
} else {
ratio = wr;
translateX = 0.0;
translateY = (size.height - (ratio * image.height)) / 2;
}
canvas.translate(translateX, translateY);
canvas.scale(ratio, ratio);
canvas.drawImage(image, new Offset(0.0, 0.0), new Paint());
}
#override
bool shouldRepaint(UiImagePainter other) {
return other.image != image;
}
}
class UiImageDrawer extends StatelessWidget {
final ui.Image image;
const UiImageDrawer({Key key, this.image}) : super(key: key);
#override
Widget build(BuildContext context) {
return CustomPaint(
size: Size.infinite,
painter: UiImagePainter(image),
);
}
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
GlobalKey<OverRepaintBoundaryState> globalKey = GlobalKey();
ui.Image image;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(),
body: image == null
? Capturer(
overRepaintKey: globalKey,
)
: UiImageDrawer(image: image),
floatingActionButton: image == null
? FloatingActionButton(
child: Icon(Icons.camera),
onPressed: () async {
var renderObject = globalKey.currentContext.findRenderObject();
RenderRepaintBoundary boundary = renderObject;
ui.Image captureImage = await boundary.toImage();
setState(() => image = captureImage);
},
)
: FloatingActionButton(
onPressed: () => setState(() => image = null),
child: Icon(Icons.remove),
),
),
);
}
}
class Capturer extends StatelessWidget {
static final Random random = Random();
final GlobalKey<OverRepaintBoundaryState> overRepaintKey;
const Capturer({Key key, this.overRepaintKey}) : super(key: key);
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: OverRepaintBoundary(
key: overRepaintKey,
child: RepaintBoundary(
child: Column(
children: List.generate(
30,
(i) => Container(
color: Color.fromRGBO(random.nextInt(256), random.nextInt(256), random.nextInt(256), 1.0),
height: 100,
),
),
),
),
),
);
}
}
class OverRepaintBoundary extends StatefulWidget {
final Widget child;
const OverRepaintBoundary({Key key, this.child}) : super(key: key);
#override
OverRepaintBoundaryState createState() => OverRepaintBoundaryState();
}
class OverRepaintBoundaryState extends State<OverRepaintBoundary> {
#override
Widget build(BuildContext context) {
return widget.child;
}
}
What it's doing is making a scroll view that encapsulates the list (column), and making sure the repaintBoundary is around the column. With your code where you use a list, there's no way it can ever capture all the children as the list is essentially a repaintBoundary in and of itself.
Note in particular the 'overRepaintKey' and OverRepaintBoundary. You might be able to get away without using it by iterating through render children, but it makes it a lot easier.
There is a simple way
You need wrap SingleChildScrollView Widget to RepaintBoundary. just wrap your Scrollable widget (or his father) with SingleChildScrollView
SingleChildScrollView(
child: RepaintBoundary(
key: _globalKey
)
)
I have a Hero that has a sub-element I want to change size as part of the hero animation (in this case, I want its aspect ratio to change).
Here's my (stripped down) build method:
Widget build(BuildContext build) {
final aspect = expanded ? 1.3 : 1.0;
return new Hero(
child: Column(
children: <Widget>[
new AspectRatio(
aspect: aspect,
child: // Stuff that creates a header
),
new Container(
child: // General descriptions
)
],
);
}
Right now, the aspect ratio jumps between the two values. My question is, can I animate aspect along with the hero animation, and have it slowly shift from 1.3 to 1.0 and back?
For a complete example (which also shows a weird overflow issue during a hero animation) see this Gist:
https://gist.github.com/fuzzybinary/74196bccecc6dd070b35474c4c9223d7
Since I'm in my house right now I'm not able to try out your code right now. But I have a working hero animation example with me. Try this out.
// main.dart class
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart' show timeDilation;
import 'package:flutter_test7/photo_hero.dart';
import 'package:flutter_test7/second_page.dart';
class HeroAnimation extends StatelessWidget {
Widget build(BuildContext context) {
timeDilation = 2.5; // 1.0 means normal animation speed.
return new Scaffold(
appBar: new AppBar(
title: const Text('Basic Hero Animation'),
),
body: new Center(
child: new PhotoHero(
photo: 'images/flippers-alpha.png',
width: 300.0,
onTap: () {
Navigator.of(context).pushNamed('/second_page');
},
),
),
);
}
}
void main() {
runApp(
new MaterialApp(
home: new HeroAnimation(),
routes: <String, WidgetBuilder>{
'/second_page': (context) => new SecondPage()
},
),
);
}
// photo_hero.dart class
import 'package:flutter/material.dart';
class PhotoHero extends StatelessWidget {
const PhotoHero({Key key, this.photo, this.onTap, this.width})
: super(key: key);
final String photo;
final VoidCallback onTap;
final double width;
Widget build(BuildContext context) {
return new SizedBox(
width: width,
child: new Hero(
tag: photo,
child: new Material(
color: Colors.transparent,
child: new InkWell(
onTap: onTap,
child: new Image.asset(
photo,
fit: BoxFit.contain,
),
),
),
),
);
}
}
// second_page.dart class
import 'package:flutter/material.dart';
import 'package:flutter_test7/photo_hero.dart';
class SecondPage extends StatefulWidget {
#override
_SecondPageState createState() => new _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: const Text('Flippers Page'),
),
body: new Container(
color: Colors.lightBlueAccent,
padding: const EdgeInsets.all(16.0),
alignment: Alignment.topLeft,
child: new PhotoHero(
photo: 'images/flippers-alpha.png',
width: 100.0,
onTap: () {
Navigator.of(context).pop();
},
),
),
);
}
}
Hope this helps.
I have a button that has an image as as a child. I want it so that while the button is being pressed, the image changes to a different one, and when the user stops pressing the button, it goes back to its original image.
Basically I want it to act like a raised button but with custom images for the raised and pressed states.
Here's the relevant code:
class LoginButton extends StatefulWidget {
#override
_LoginButtonState createState() => new _LoginButtonState();
}
class _LoginButtonState extends State<LoginButton> {
void _onClicked() {
setState(() {
//I don't know what I should put here to cause the image to redraw
//only on button press
});
}
#override
Widget build(BuildContext context) {
var assetImage = new AssetImage("assets/loginscreen/btn.png");
var image = new Image(image: assetImage, height: 50.0, width: 330.0);
return new Container(
height: image.height,
width: image.width,
child: new FlatButton(
onPressed: _onClicked,
child: new ConstrainedBox(
constraints: new BoxConstraints.expand(),
child: image,
),
),
);
}
}
You can define your default image as a property of the class _LoginButtonStatelike this
class _LoginButtonState extends State<LoginButton> {
String _myImage = "assets/loginscreen/btn.png"; ... }
Now your _onClick method should contain the change of the state, simply you can change the string of _myImage to the new image using an if condition
void _onClicked() {
setState(() {
if (_myImage == "assets/loginscreen/btn.png"){
_myImage = "<my new asset>"; //change myImage to the other one
}
else {
_myImage = "assets/loginscreen/btn.png"; //change myImage back to the original one
}
});
}
and within your widget build:
var assetImage = new AssetImage(_myImage);
===========================================================================
Update
I have managed to do a similar idea to what you are trying to achieve using GesutureDetector , here I tested it with colors, but it should not be so different from changing a link of an image, although I presume repainting an image would be slower than changing colors.
Here is the full code I used:
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
home: new MyApp (),
));
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
var _myColor = Colors.blue;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text ("Tap me!"),
centerTitle: true,
),
body: new GestureDetector(
onTapDown:(TapDownDetails details) { setState(() {
_myColor = Colors.orange;
});
},
onTapUp: (TapUpDetails details) {
setState(() {
_myColor = Colors.blue;
});
},
child: new Container (
color: _myColor,
),
),
);
}
}
For me i wanted a button with no extra padding and would be darken when i press it!
Here is the code that work for me:
Container(
height: image.height,
width: image.width,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('/images/image.jpeg'),
),
),
child: new FlatButton(
padding: EdgeInsets.all(0.0),
onPressed: () {},
child: null),
),
Credits to https://www.youtube.com/watch?v=Ze-BZBv85Ck! This video help me for this! :)
Let's say I have a rectangular, portrait image:
I'd like to crop it, such that it's rendered like this:
How can I do this in Flutter?
(I don't need to resize the image.)
(Image from https://flic.kr/p/nwXTDb)
I would probably use a BoxDecoration with a DecorationImage. You can use the alignment and fit properties to determine how your image is cropped. You can use an AspectRatio widget if you don't want to hard code a height on the Container.
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
home: new MyHomePage(),
));
}
class MyHomePage extends StatelessWidget {
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Image Crop Example"),
),
body: new Center(
child: new AspectRatio(
aspectRatio: 487 / 451,
child: new Container(
decoration: new BoxDecoration(
image: new DecorationImage(
fit: BoxFit.fitWidth,
alignment: FractionalOffset.topCenter,
image: new NetworkImage('https://i.stack.imgur.com/lkd0a.png'),
)
),
),
),
),
);
}
}
You can also directly use the Image class with BoxFit and do something like:
new Image.asset(
stringToImageLocation,
fit: BoxFit.cover,
)
Provide a fit factor to your Image widget and then wrap it in AspectRatio.
AspectRatio(
aspectRatio: 1.5,
child: Image.asset(
'your_image_asset',
fit: BoxFit.cover,
),
)
Take a look to brendan-duncan/image, it's platform-independent library to manipulate images in Dart.
You can use the function:
Image copyCrop(Image src, int x, int y, int w, int h);
Worked for me using just these 2 properties:
CachedNetworkImage(
fit: BoxFit.cover,// OR BoxFit.fitWidth
alignment: FractionalOffset.topCenter,
....
)
There is a new package called ImageCropper. I would recommend everyone to use this instead as it has many features and makes everything easier. It allows you to crop the image to any or specified aspect ratio you want and can even compress the image. Here is the link to the package: https://pub.dev/packages/image_cropper
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:image_cropper/image_cropper.dart';
class MyPage extends StatefulWidget {
#override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
/// Variables
File imageFile;
/// Widget
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Color(0XFF307777),
title: Text("Image Cropper"),
),
body: Container(
child: imageFile == null
? Container(
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
color: Color(0XFF307777),
onPressed: () {
_getFromGallery();
},
child: Text(
"PICK FROM GALLERY",
style: TextStyle(color: Colors.white),
),
),
],
),
)
: Container(
child: Image.file(
imageFile,
fit: BoxFit.cover,
),
)));
}
/// Get from gallery
_getFromGallery() async {
PickedFile pickedFile = await ImagePicker().getImage(
source: ImageSource.gallery,
maxWidth: 1800,
maxHeight: 1800,
);
_cropImage(pickedFile.path);
}
/// Crop Image
_cropImage(filePath) async {
File croppedImage = await ImageCropper.cropImage(
sourcePath: filePath,
maxWidth: 1080,
maxHeight: 1080,
);
if (croppedImage != null) {
imageFile = croppedImage;
setState(() {});
}
}
}
Here I crop file to square.
I use image library.
import 'dart:io';
import 'package:image/image.dart' as img;
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
class CropperService {
static const _side = 1800;
Future<File> cropImageFile(File file, [int? side]) async {
final image = await img.decodeImageFile(file.path);
if (image == null) throw Exception('Unable to decode image');
final croppedImage = img.copyResizeCropSquare(image, size: _side);
final croppedFile = await _convertImageToFile(croppedImage, file.path);
return croppedFile;
}
Future<File> _convertImageToFile(img.Image image, String path) async {
final newPath = await _croppedFilePath(path);
final jpegBytes = img.encodeJpg(image);
final convertedFile = await File(newPath).writeAsBytes(jpegBytes);
await File(path).delete();
return convertedFile;
}
Future<String> _croppedFilePath(String path) async {
final tempDir = await getTemporaryDirectory();
return join(
tempDir.path,
'${basenameWithoutExtension(path)}_compressed.jpg',
);
}
}