Flutter: How to add a zoom functionality to image - flutter

In my flutter app, a custom image is built with the following code
customImage.dart
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
Widget cachedNetworkImage(mediaUrl) {
return CachedNetworkImage(
imageUrl: mediaUrl,
fit: BoxFit.cover,
placeholder: (context, url)=>
Padding(padding: EdgeInsets.all(20.0),
child: CircularProgressIndicator(),
),
errorWidget: (context, url, error) => Icon(Icons.error) ,
);
}
I want to add a functionality to pinch to zoom on this image,how can I achieve that?

You can copy paste run full code below
You can use Flutter's bulid-in InteractiveViewer https://api.flutter.dev/flutter/widgets/InteractiveViewer-class.html
A widget that enables pan and zoom interactions with its child.
working demo
full code
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
Widget cachedNetworkImage(mediaUrl) {
return CachedNetworkImage(
imageUrl: mediaUrl,
fit: BoxFit.cover,
placeholder: (context, url) => Padding(
padding: EdgeInsets.all(20.0),
child: CircularProgressIndicator(),
),
errorWidget: (context, url, error) => Icon(Icons.error),
);
}
void main() => runApp(MyApp());
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: MyStatelessWidget(),
),
);
}
}
/// This is the stateless widget that the main application instantiates.
class MyStatelessWidget extends StatelessWidget {
MyStatelessWidget({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: InteractiveViewer(
boundaryMargin: EdgeInsets.all(20.0),
minScale: 0.1,
maxScale: 1.6,
child: cachedNetworkImage("https://picsum.photos/250?image=9"),
),
);
}
}

Use photo_view for this. It allows you to create zoomable image widgets without dealing with pinch gesture or sth.
import 'package:photo_view/photo_view.dart';
#override
Widget build(BuildContext context) {
return Container(
child: PhotoView(
imageProvider: AssetImage("assets/image.jpg"),
)
);
}
Source: https://pub.dev/packages/photo_view

Related

How to access variable from different class ? Flutter (I want to change background image onpress)

I'm struggling to find way to access variable from different class. I want to change background image of decorationImage on button click. But because I do not want to rebuild entire state/page just the background I put the code to different class, but I'm not sure how to access _imagepath1 or _imagepath2 from other class.
class CustomMainPageState extends State<SecondRoute> {
Color myColor = Color(0xff5D6592);
String _imagepath1 = "images/image3.jpg";
String _imagepath2 = "images/image4.jpg";
Widget build(BuildContext context) => Container(
//color: Colors.transparent,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(_imagepath1), fit: BoxFit.cover)),
child: Scaffold(
class background extends StatefulWidget {
backgroundG createState() => backgroundG();
}
class backgroundG extends State<background> {
#override
Widget buildbottom() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
CircleAvatar(
radius: 25,
backgroundColor: Colors.white,
child: IconButton(
icon: Image.asset('images/icon2.png'),
onPressed: () => setState(() {
//_imagefile1 = _imagefile2;
}),
),
),
]);
}
#override
Widget build(BuildContext context) {
return Container(
child: Row(
children: [
SizedBox(height: 100),
buildbottom(),
//SizedBox(height: 10),
//SizedBox(height: 10),//puts empty box to space things out
],
),
);
}
}
Thank you for helping!
For passing one image to different class you can use flutter hero widget,
below is sample code of working with Hero widget check documentation also for more info about hero widget
import 'package:flutter/material.dart';
void main() => runApp(HeroApp());
class HeroApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Transition Demo',
home: MainScreen(),
);
}
}
class MainScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Main Screen'),
),
body: GestureDetector(
child: Hero(
tag: 'imageHero',
child: Image.network(
'https://picsum.photos/250?image=9',
),
),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return DetailScreen();
}));
},
),
);
}
}
class DetailScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
child: Center(
child: Hero(
tag: 'imageHero',
child: Image.network(
'https://picsum.photos/250?image=9',
),
),
),
onTap: () {
Navigator.pop(context);
},
),
);
}
}
for passing data between widgets or class I would suggest you to use InheritedWidget class or any state management like provider, bloc, getx to know more about this check this video and documentation and if you just want to pass single image then refer this blog

Flutter image builder callback issue

I have a problem with using the Image error builder. For example I want to change another widget in the tree to not have a colour. I thought about using a boolean flag but it seems messy. Is there a simple way to do this. Below is an example of what i mean
return Stack(
children: [
Image.file(
File("Some path"),
errorBuilder: (BuildContext context, Object exception, StackTrace stackTrace) {
// If error builder draws i want the container below colour to become transparent...
// how do i do this?
return Text('Error');
},
),
Container(
height: 100,
width: 200,
color: Colors.red,
),
],
);
You can copy paste run full code below
You can use StreamBuilder and call _events.add(Colors.transparent); in errorBuilder
In working demo, I use image.network to simulate your case, you can directly modify to Image.file
code snippet
Image.file(
File(widget.path),
errorBuilder:
(BuildContext context, Object exception, StackTrace stackTrace) {
_events.add(Colors.transparent);
return Text('Error');
},
),
StreamBuilder<Color>(
stream: _events.stream,
builder: (BuildContext context, AsyncSnapshot<Color> snapshot) {
return Container(
height: 100,
width: 200,
color: snapshot.data,
);
})
working demo
full code
import 'package:flutter/material.dart';
import 'dart:async';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ImageHandelError(
path: 'https://picsum.photos/250?image=9',
),
ImageHandelError(
path: 'not exist',
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class ImageHandelError extends StatefulWidget {
String path;
ImageHandelError({this.path});
#override
_ImageHandelErrorState createState() => _ImageHandelErrorState();
}
class _ImageHandelErrorState extends State<ImageHandelError> {
StreamController<Color> _events;
#override
initState() {
super.initState();
_events = StreamController<Color>();
_events.add(Colors.red);
}
#override
Widget build(BuildContext context) {
return Stack(
children: [
Image.network(
widget.path,
errorBuilder:
(BuildContext context, Object exception, StackTrace stackTrace) {
_events.add(Colors.transparent);
return Text('Error');
},
),
StreamBuilder<Color>(
stream: _events.stream,
builder: (BuildContext context, AsyncSnapshot<Color> snapshot) {
return Container(
height: 100,
width: 200,
color: snapshot.data,
);
})
],
);
}
}
I would advice you to use an empty image to fill the gap when ever error happen
child: FadeInImage.assetNetwork(
image: "https://cdn-icons-png.flaticon.com/512/270/270014.png",
fit: BoxFit.fitWidth,
placeholder: Assets.logo_place_holder,//this the image you have prepared
imageErrorBuilder: (_, __, ___) {
return Image.asset(Assets.logo_place_holder); //this is what will fill the Container in case error happened
},
),
or you can Just return a Container and assign color to it and it will spared to fill out the area
this is what it will look like
and this what its look like with Container
this is what it will look like
return Container(color: Colors.red,);

How to zoom image in flutter with CachedNetworkImage widget

can you suggest a way to zoom an image inside a CachedNetworkImage?
Here is my code
CachedNetworkImage(
imageUrl: "http://via.placeholder.com/350x150",
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
),
I tried to wrap CachedNetworkImage in a photo_view widget but it does not work
#override
Widget build(BuildContext context) {
return Container(
child: PhotoView(
imageProvider: CachedNetworkImage(
imageUrl: "http://via.placeholder.com/350x150",
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
)
)
);
}
You can copy paste run full code below
Package Cached network image provide CachedNetworkImageProvider
code snippet
PhotoView(
imageProvider:
CachedNetworkImageProvider("http://via.placeholder.com/350x150"),
)
working demo
full code
import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart';
import 'package:cached_network_image/cached_network_image.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(flex: 1, child: PhotoViewTest()),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class PhotoViewTest extends StatefulWidget {
#override
_PhotoViewTestState createState() => _PhotoViewTestState();
}
class _PhotoViewTestState extends State<PhotoViewTest> {
#override
Widget build(BuildContext context) {
return Container(
child: PhotoView(
imageProvider:
CachedNetworkImageProvider("http://via.placeholder.com/350x150"),
),
);
}
}
You can wrap Photo view inside Cached Network image like this code, so you can use advantages of both cached network image and photo view
CachedNetworkImage(
imageUrl: "http://via.placeholder.com/350x150",
imageBuilder: (context, imageProvider) => PhotoView(
imageProvider: imageProvider,
),
placeholder: (context, url) =>
CircularProgressIndicator(),
errorWidget: (context, url, error) =>
Icon(Icons.error),
)
Widget build(BuildContext context) {
return Container(
child: CachedNetworkImage(
imageUrl: "http://via.placeholder.com/350x150",
imageBuilder: (context, imageProvider) => PhotoView(
imageProvider: imageProvider,
)
),
);
}

Flutter. How can I make container wider than screen?

I'm trying to create a parallax background for page controller. For that purpuse I need to create a background image that is wider than the screen. I've put it inside a container like this:
#override
Widget build(BuildContext context) {
return Material(
child: Stack(
children: [
Container(
width: 4000,
height: 250,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/pizza_bg.png'),
fit: BoxFit.cover,
repeat: ImageRepeat.noRepeat
)
)
),
],
),
);
}
But the problem is that no matter what width I specify, the container (and the image, of course) never get wider than the screen. Is it possible at all?
p.s. I tried to use SizedBox and AspectRatio widgets, and they both give the same result
try this, as an option
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Stack(
children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Container(
width: 4000,
height: 250,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/pizza_bg.png'),
fit: BoxFit.cover,
repeat: ImageRepeat.noRepeat,
),
),
),
),
],
),
),
);
}
}
also you can disable scroll for user and manage scroll position via scroll controller
SingleChildScrollView(
scrollDirection: Axis.horizontal,
physics: const NeverScrollableScrollPhysics(),
controller: controller, // your ScrollController
child: Container(
width: 4000,
height: 250,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('images/pizza_bg.png'),
fit: BoxFit.cover,
repeat: ImageRepeat.noRepeat,
),
),
),
),
For images you can use Transform.scale(), as found in the documentation. Using your example:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Stack(
children: [
Align(
alignment: Alignment.center,
child: Transform.scale(
scale: 10.0,
child: Container(
width: 400,
height: 25,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/pizza_bg.png'),
fit: BoxFit.cover,
repeat: ImageRepeat.noRepeat,
),
),
),
),
),
],
),
),
);
}
}
If you want to animate the scale, you can use ScaleTransition(), explained in this page of the docs. For example:
/// Flutter code sample for ScaleTransition
// The following code implements the [ScaleTransition] as seen in the video
// above:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
/// AnimationControllers can be created with `vsync: this` because of TickerProviderStateMixin.
class _MyStatefulWidgetState extends State<MyStatefulWidget>
with TickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
#override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
)..repeat(reverse: true);
_animation = CurvedAnimation(
parent: _controller,
curve: Curves.fastOutSlowIn,
);
}
#override
void dispose() {
super.dispose();
_controller.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ScaleTransition(
scale: _animation,
child: const Padding(
padding: EdgeInsets.all(8.0),
child: FlutterLogo(size: 150.0),
),
),
),
);
}
}
NOTE: To avoid quality loss in the image, use an image of the size after scaling or a vector graphic as a source.

flutter CachedNetworkImage doesn't work as expected

I'm trying to work with cached images.
I followed this https://flutter.dev/docs/cookbook/images/cached-images,
but it doesn't work.
CachedNetworkImage throws error: "The argument type 'CircularProgressIndicator' can't be assigned to the parameter type '(BuildContext, String) → Widget'. (argument_type_not_assignable at [hello2] lib/main.dart:21)"
Below is the problem code:
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final title = 'Cached Images';
return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: CachedNetworkImage(
placeholder: CircularProgressIndicator(),
imageUrl:
'https://picsum.photos/250?image=9',
),
),
),
);
}
}
```dart
reinstall your application and then run again will work, it worked for me
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
class App extends StatelessWidget {
const App({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
final title = 'Cached Images';
return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: CachedNetworkImage(
placeholder: (context, url) => CircularProgressIndicator(),
imageUrl: 'https://picsum.photos/250?image=9',
),
),
),
);
}
}
It works after changing:
placeholder: CircularProgressIndicator(),
to:
placeholder: (context, url) => new CircularProgressIndicator(),
BTW: cached_network_image had a breaking change in 0.6.0.
The tutorial link (https://flutter.dev/docs/cookbook/images/cached-images) is out of date.
I would like to add explanation to the above agreed answer.
placeholder is of type Widget Function(BuildContext, String)? placeholder
So it throws you error:
The argument type CircularProgressIndicator can't be assigned to the parameter type (BuildContext, String) → Widget
Convert:
placeholder: CircularProgressIndicator()
to
placeholder: (context, url) => new CircularProgressIndicator(),