I am trying to display all the phone gallery images myself by reading the external files directory and possibly every image that ends with jpg or png. I achieved that, but could not display all of them in a grid as due to their sizes or the no. of images, the app crashes. Code looks bit like this..
new GridView.count(
shrinkWrap: true,
physics: new ClampingScrollPhysics(),
crossAxisCount: 2,
// children: new List<Widget>.generate(_images.length, (index) {
// children: new List<Widget>.generate(allImages.length, (index) {
children: new List<Widget>.generate(_PhoneImages.length, (index) {
File imgFile = _phoneImageFiles[index];
thumbBytes = _phoneThumbBytes[index]; // assuming it got created!!!
// print('thumbbytes $thumbBytes');
print('phone image index: $index');
return new GridTile(
child: new GestureDetector(
child: new Stack(
children: [
new Card(
// color: Colors.blue.shade200,
color: Colors.white70,
child: new Center(
// child: new Text('tile $index'),
// child: new Image.asset(_images[index]),
/*
child: new CachedNetworkImage(
imageUrl: allImages[index].path,
// placeholder: new CircularProgressIndicator(),
errorWidget: new Icon(Icons.error),
)
*/
child: new Image.file(imgFile,
// child: new Image.memory(thumbBytes,
So I tried the imageresize library which tells me to do a heavy operation of resizing, that takes almost 20 minutes before I can show the thumbnails.
All I need is to read thumbnails from gallery like how the phone gallery displays. I don't need categorization. I need all and a link to their full version so that I can do something with them later on.
I think this might help multi_image_picker
e.g
import 'package:flutter/material.dart';
import 'package:multi_image_picker/asset.dart';
class AssetView extends StatefulWidget {
final int _index;
final Asset _asset;
AssetView(this._index, this._asset);
#override
State<StatefulWidget> createState() => AssetState(this._index, this._asset);
}
class AssetState extends State<AssetView> {
int _index = 0;
Asset _asset;
AssetState(this._index, this._asset);
#override
void initState() {
super.initState();
_loadImage();
}
void _loadImage() async {
await this._asset.requestThumbnail(300, 300); // here requesting thumbnail
setState(() {});
}
#override
Widget build(BuildContext context) {
if (null != this._asset.thumbData) {
return Image.memory(
this._asset.thumbData.buffer.asUint8List(),
fit: BoxFit.cover,
);
}
return Text(
'${this._index}',
style: Theme.of(context).textTheme.headline,
);
}
}
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:multi_image_picker/asset.dart';
import 'package:multi_image_picker/multi_image_picker.dart';
import 'asset_view.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<Asset> images = List<Asset>();
String _error;
#override
void initState() {
super.initState();
}
Widget buildGridView() {
return GridView.count(
crossAxisCount: 3,
children: List.generate(images.length, (index) {
return AssetView(index, images[index]);
}),
);
}
Future<void> loadAssets() async {
setState(() {
images = List<Asset>();
});
List resultList;
String error;
try {
resultList = await MultiImagePicker.pickImages(
maxImages: 300,
);
} on PlatformException catch (e) {
error = e.message;
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
images = resultList;
if (error == null) _error = 'No Error Dectected';
});
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: const Text('Plugin example app'),
),
body: Column(
children: <Widget>[
Center(child: Text('Error: $_error')),
RaisedButton(
child: Text("Pick images"),
onPressed: loadAssets,
),
Expanded(
child: buildGridView(),
)
],
),
),
);
}
}
Related
I'm trying to create a basic Hero animation between a gallery grid view page (using photo manager plugin) and a detail page. When the hero animation is done both back and forward, the picture is flickering .
Here is the example I've made :
The full code runnable :
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:photo_manager/photo_manager.dart';
void main() {
runApp(const TestApp());
}
class TestApp extends StatefulWidget {
const TestApp({Key? key}) : super(key: key);
#override
State<TestApp> createState() => _TestAppState();
}
class _TestAppState extends State<TestApp> {
final List<AssetEntity> assetsList = [];
bool granted = false;
void loadAssets() async {
granted = await PhotoManager.requestPermission();
if (granted) {
FilterOptionGroup option = FilterOptionGroup()
..addOrderOption(const OrderOption(
type: OrderOptionType.createDate,
asc: false,
));
final albums = await PhotoManager.getAssetPathList(filterOption: option, type: RequestType.image);
print("albums : $albums");
if (albums.isNotEmpty) {
var alb = albums.where((element) {
return element.name == 'Test';
});
var album = alb.first;
// Now that we got the album, fetch all the assets it contains
List<AssetEntity> currentList =
await album.getAssetListRange(start: 0, end: 200);
// Update the state and notify UI
assetsList.clear();
assetsList.addAll(currentList);
}
setState(() {});
}
}
#override
void initState() {
loadAssets();
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: granted
? _gridView()
: Center(
child: Container(
color: Colors.blue,
width: 200,
height: 200,
child: TextButton(
onPressed: () async {
granted = await PhotoManager.requestPermission();
setState(() {});
},
child: const Text(
"Ask permission",
style: TextStyle(color: Colors.white),
),
)),
)),
);
}
Widget _gridView() {
return GridView.builder(
itemCount: assetsList.length,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
itemBuilder: (context, index) {
return Hero(
createRectTween: (Rect? begin, Rect? end) {
RectTween _rectTween = RectTween(begin: begin, end: end);
return _rectTween;
},
tag: assetsList[index].id,
child: GalleryThumbnail(
asset: assetsList[index],
onTap: (bytes) {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return DetailsPage(asset: assetsList[index], bytes: bytes);
}));
},
));
});
}
}
class GalleryThumbnail extends StatelessWidget {
final AssetEntity asset;
final ValueChanged<Uint8List> onTap;
const GalleryThumbnail({Key? key, required this.asset, required this.onTap})
: super(key: key);
#override
Widget build(BuildContext context) {
return FutureBuilder<Uint8List?>(
future: Platform.isIOS
? asset.thumbDataWithOption(
ThumbOption.ios(
width: 500,
height: 500,
deliveryMode: DeliveryMode.opportunistic,
resizeMode: ResizeMode.fast,
resizeContentMode: ResizeContentMode.fit,
quality: 100
// resizeContentMode: ResizeContentMode.fill,
),
)
: asset.thumbDataWithSize(250, 250),
builder: (_, snapshot) {
final bytes = snapshot.data;
if (snapshot.hasError) {
return Container();
}
// If we have no data
if (bytes == null) return Container();
// If there's data, display it as an image
return GestureDetector(
onTap: () {
onTap(bytes);
},
child: Image.memory(bytes, fit: BoxFit.cover,gaplessPlayback: true,));
},
);
}
}
class DetailsPage extends StatefulWidget {
final AssetEntity asset;
final Uint8List bytes;
const DetailsPage({Key? key, required this.asset, required this.bytes})
: super(key: key);
#override
_DetailsPageState createState() => _DetailsPageState();
}
class _DetailsPageState extends State<DetailsPage> {
late ImageProvider _imageProvider;
Future<void> loadFile() async {
try {
File? file = await widget.asset.file;
if (file == null) return;
_imageProvider = Image.file(file).image;
setState(() {});
} catch (e) {
print("error to load file : " + e.toString());
}
}
#override
void initState() {
_imageProvider = Image.memory(widget.bytes).image;
loadFile();
super.initState();
}
#override
Widget build(BuildContext context) {
double ratio = widget.asset.height / widget.asset.width;
return Scaffold(
body: Stack(
children: [
Hero(
createRectTween: (Rect? begin, Rect? end) {
RectTween _rectTween = RectTween(begin: begin, end: end);
return _rectTween;
},
tag: widget.asset.id,
child: Center(
child: Image(image: _imageProvider,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height*ratio,
gaplessPlayback: true),
),
),
const SafeArea(
child: Padding(
padding: EdgeInsets.all(8.0),
child: BackButton(),
)),
],
),
);
}
}
I've implemented hero animation somewhere else in my project and I'm not getting this behavior where as here I do.
Why this is happening and how to correct this ?
Add
transitionOnUserGestures: true,
to Hero() widget in current & next page
This happens because the Hero of the image thumbail wraps the FutureBuilder, and then the future triggers again during the animation, wraps the Image instead of the FutureBuilder.
I am using https://pub.dev/packages/gallery_saver for saving compressed images in a folder named Image Resizer now I want to open that saved images folder
You can use the image_picker plugin in Flutter for opening the images from the device. It will provide an option to open the gallery through which you can open the folder of saved images.
You can get the image_picker plugin here.
below is the solution i did it
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
//import 'package:ext_storage/ext_storage.dart';
import 'ext_storage.dart';
class SavedFiles extends StatefulWidget {
#override
_SavedFilesState createState() => _SavedFilesState();
}
class _SavedFilesState extends State<SavedFiles> {
List<dynamic> listImagePath = List<dynamic>();
Future<String> _getPath() {
return ExtStorage.getExternalStoragePublicDirectory(ExtStorage.PICTURES);
}
_fetchFiles(Directory dir) {
List<dynamic> listImage = List<dynamic>();
dir.list().forEach((element) {
RegExp regExp =
new RegExp("\.(gif|jpe?g|tiff?|png|webp|bmp)", caseSensitive: false);
// Only add in List if path is an image
if (regExp.hasMatch('$element')) listImage.add(element);
setState(() {
listImagePath = listImage;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Saved Images'),backgroundColor: Colors.orange,),
body: Column(
children: [
Expanded(
child: FutureBuilder(
future: _getPath(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
// print('data');
var dir = Directory(snapshot.data);
_fetchFiles(dir);
// return Text(snapshot.data);
return Container();
} else {
return Text("Loading");
}
},
),
),
// Expanded(child: Text('sss')),
Expanded(
flex: 19,
child: GridView.count(
primary: false,
padding: const EdgeInsets.all(5),
crossAxisSpacing: 2,
mainAxisSpacing: 2,
crossAxisCount: 3,
children: _getListImg(listImagePath),
),
)
],
)
);
}
List<Widget> _getListImg(List<dynamic> listImagePath) {
List<Widget> listImages = List<Widget>();
for (var imagePath in listImagePath) {
listImages.add(
Container(
padding: const EdgeInsets.all(8),
child: Image.file(imagePath, fit: BoxFit.cover),
),
);
}
return listImages;
}
}
I tried to find how to select and upload multiple images in flutter but most of the plugins are not working or I did not understand them well. I found little application but it selects and upload only one picture. How to change this code that user can select and upload multiple pictures or is there any other alternatives. Please write in details, i am freshman in coding. Thanks in advance.
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
import 'package:mime/mime.dart';
import 'dart:convert';
import 'package:http_parser/http_parser.dart';
import 'package:toast/toast.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: 'Image Upload Demo',
theme: ThemeData(primarySwatch: Colors.pink),
home: ImageInput());
}
}
class ImageInput extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _ImageInput();
}
}
class _ImageInput extends State<ImageInput> {
// To store the file provided by the image_picker
File _imageFile;
// To track the file uploading state
bool _isUploading = false;
String baseUrl = 'http://YOUR_IPV4_ADDRESS/flutterdemoapi/api.php';
void _getImage(BuildContext context, ImageSource source) async {
File image = await ImagePicker.pickImage(source: source);
setState(() {
_imageFile = image;
});
// Closes the bottom sheet
Navigator.pop(context);
}
Future<Map<String, dynamic>> _uploadImage(File image) async {
setState(() {
_isUploading = true;
});
// Find the mime type of the selected file by looking at the header bytes of the file
final mimeTypeData =
lookupMimeType(image.path, headerBytes: [0xFF, 0xD8]).split('/');
// Intilize the multipart request
final imageUploadRequest =
http.MultipartRequest('POST', Uri.parse(baseUrl));
// Attach the file in the request
final file = await http.MultipartFile.fromPath('image', image.path,
contentType: MediaType(mimeTypeData[0], mimeTypeData[1]));
// Explicitly pass the extension of the image with request body
// Since image_picker has some bugs due which it mixes up
// image extension with file name like this filenamejpge
// Which creates some problem at the server side to manage
// or verify the file extension
imageUploadRequest.fields['ext'] = mimeTypeData[1];
imageUploadRequest.files.add(file);
try {
final streamedResponse = await imageUploadRequest.send();
final response = await http.Response.fromStream(streamedResponse);
if (response.statusCode != 200) {
return null;
}
final Map<String, dynamic> responseData = json.decode(response.body);
_resetState();
return responseData;
} catch (e) {
print(e);
return null;
}
}
void _startUploading() async {
final Map<String, dynamic> response = await _uploadImage(_imageFile);
print(response);
// Check if any error occured
if (response == null || response.containsKey("error")) {
Toast.show("Image Upload Failed!!!", context,
duration: Toast.LENGTH_LONG, gravity: Toast.BOTTOM);
} else {
Toast.show("Image Uploaded Successfully!!!", context,
duration: Toast.LENGTH_LONG, gravity: Toast.BOTTOM);
}
}
void _resetState() {
setState(() {
_isUploading = false;
_imageFile = null;
});
}
void _openImagePickerModal(BuildContext context) {
final flatButtonColor = Theme.of(context).primaryColor;
print('Image Picker Modal Called');
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
height: 150.0,
padding: EdgeInsets.all(10.0),
child: Column(
children: <Widget>[
Text(
'Pick an image',
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(
height: 10.0,
),
FlatButton(
textColor: flatButtonColor,
child: Text('Use Camera'),
onPressed: () {
_getImage(context, ImageSource.camera);
},
),
FlatButton(
textColor: flatButtonColor,
child: Text('Use Gallery'),
onPressed: () {
_getImage(context, ImageSource.gallery);
},
),
],
),
);
});
}
Widget _buildUploadBtn() {
Widget btnWidget = Container();
if (_isUploading) {
// File is being uploaded then show a progress indicator
btnWidget = Container(
margin: EdgeInsets.only(top: 10.0),
child: CircularProgressIndicator());
} else if (!_isUploading && _imageFile != null) {
// If image is picked by the user then show a upload btn
btnWidget = Container(
margin: EdgeInsets.only(top: 10.0),
child: RaisedButton(
child: Text('Upload'),
onPressed: () {
_startUploading();
},
color: Colors.pinkAccent,
textColor: Colors.white,
),
);
}
return btnWidget;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Image Upload Demo'),
),
body: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 40.0, left: 10.0, right: 10.0),
child: OutlineButton(
onPressed: () => _openImagePickerModal(context),
borderSide:
BorderSide(color: Theme.of(context).accentColor, width: 1.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.camera_alt),
SizedBox(
width: 5.0,
),
Text('Add Image'),
],
),
),
),
_imageFile == null
? Text('Please pick an image')
: Image.file(
_imageFile,
fit: BoxFit.cover,
height: 300.0,
alignment: Alignment.topCenter,
width: MediaQuery.of(context).size.width,
),
_buildUploadBtn(),
],
),
);
}
}
I used that package:
dependencies:
multi_image_picker: ^4.6.7
Ex:
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:multi_image_picker/multi_image_picker.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<Asset> images = List<Asset>();
String _error;
#override
void initState() {
super.initState();
}
Widget buildGridView() {
if (images != null)
return GridView.count(
crossAxisCount: 3,
children: List.generate(images.length, (index) {
Asset asset = images[index];
return AssetThumb(
asset: asset,
width: 300,
height: 300,
);
}),
);
else
return Container(color: Colors.white);
}
Future<void> loadAssets() async {
setState(() {
images = List<Asset>();
});
List<Asset> resultList;
String error;
try {
resultList = await MultiImagePicker.pickImages(
maxImages: 300,
);
} on Exception catch (e) {
error = e.toString();
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
images = resultList;
if (error == null) _error = 'No Error Dectected';
});
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: const Text('Plugin example app'),
),
body: Column(
children: <Widget>[
Center(child: Text('Error: $_error')),
RaisedButton(
child: Text("Pick images"),
onPressed: loadAssets,
),
Expanded(
child: buildGridView(),
)
],
),
),
);
}
}
Add dependecy of image_picker:
image_picker: ^0.8.4+3
Then make a method for selectImages():
final ImagePicker imagePicker = ImagePicker();
List<XFile>? imageFileList = [];
void selectImages() async {
final List<XFile>? selectedImages = await
imagePicker.pickMultiImage();
if (selectedImages!.isNotEmpty) {
imageFileList!.addAll(selectedImages);
}
print("Image List Length:" + imageFileList!.length.toString());
setState((){});
}
Create a builder for showing selected Images:
return Scaffold(
appBar: AppBar(
title: Text('Multiple Images'),
),
body: SafeArea(
child: Column(
children: [
ElevatedButton(
onPressed: () {
selectImages();
},
child: Text('Select Images'),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: GridView.builder(
itemCount: imageFileList!.length,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3),
itemBuilder: (BuildContext context, int index) {
return Image.file(File(imageFileList![index].path),
fit: BoxFit.cover,);
}),
),
),
],
),
));
Complete Source code available in github link...
https://github.com/NishaJain24/multi_image_picker
Is it possible to edit image like to rotate and to add text over image. Is there any plugin for that? I need an editor for image to add texts with various fonts and colors. Thank you
you should use RepaintBoundary to capture it as a widget and to overlap widget use stack.
Look at this code how to capture widget as a image.
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
GlobalKey _globalKey = new GlobalKey();
bool inside = false;
Uint8List imageInMemory;
Future<Uint8List> _capturePng() async {
try {
print('inside');
inside = true;
RenderRepaintBoundary boundary =
_globalKey.currentContext.findRenderObject();
ui.Image image = await boundary.toImage(pixelRatio: 3.0);
ByteData byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
Uint8List pngBytes = byteData.buffer.asUint8List();
// String bs64 = base64Encode(pngBytes);
// print(pngBytes);
// print(bs64);
print('png done');
setState(() {
imageInMemory = pngBytes;
inside = false;
});
return pngBytes;
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
return RepaintBoundary(
key: _globalKey,
child: new Scaffold(
appBar: new AppBar(
title: new Text('Widget To Image demo'),
),
body: SingleChildScrollView(
child: Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
'click the button below to capture image',
),
new RaisedButton(
child: Text('capture Image'),
onPressed: _capturePng,
),
inside ? CircularProgressIndicator()
:
imageInMemory != null
? Container(
child: Image.memory(imageInMemory),
margin: EdgeInsets.all(10))
: Container(),
],
),
),
)),
);
}
}
So, I'm trying to make a home delivery app for a restaurant in flutter and I can't seem to get my hero animation working. First I made a splash screen where the logo shows up and then it navigates to home page where the logo is supposed to do a hero transition. The splash screen and the home page are in two separate dart files. Here's the code for my splash screen:
import 'package:flutter/material.dart';
import 'home_page.dart';
import 'dart:async';
class Splash extends StatefulWidget {
#override
_SplashState createState() => new _SplashState();
}
class _SplashState extends State<Splash> with SingleTickerProviderStateMixin
{
Animation<double> _mainLogoAnimation;
AnimationController _mainLogoAnimationController;
#override
void initState() {
super.initState();
goToHomePage();
_mainLogoAnimationController = new AnimationController(duration: new Duration(milliseconds: 2500) ,vsync: this);
_mainLogoAnimation = new CurvedAnimation(parent:
_mainLogoAnimationController, curve: Curves.easeIn);
_mainLogoAnimation.addListener(() => (this.setState(() {})));
_mainLogoAnimationController.forward();
}
Future goToHomePage() async {
await new Future.delayed(const Duration(milliseconds: 4000));
Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context) => new HomePage()));
}
#override
Widget build(BuildContext context) {
return new Material(
color: Colors.black,
child: new Center(
child: new Opacity(
opacity: 1.0 * _mainLogoAnimation.value,
child: new Hero(
tag: 'tbh_logo',
child: new Image(
image: new AssetImage('assets/images/tbh_main_logo.png'),
width: 300.0
)
)
)
)
);
}
}
And here's the code for the home page:
import 'package:flutter/material.dart';
import '../ui/drawer.dart';
import 'splash.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => new _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("The Barni House"),
backgroundColor: Colors.black,
),
drawer: new Drawer(child: MyDrawer()),
body: new Center(
child: Column(
children: <Widget>[
new Container(
child: new Hero(
tag: 'tbh_logo',
child: new Image(
image: new AssetImage('assets/images/tbh_main_logo.png'),
width: 300.0
)
)
)
],
)
),
);
}
}
Checked your code so the hero animation is working but its happening to fast because of the transition duration is only 300 milliseconds.
To achieve the below result you can make a Custom MaterialPageRoute.
Before
#override
Duration get transitionDuration => const Duration(milliseconds: 300);
After
#override
Duration get transitionDuration => const Duration(milliseconds: 1000);
Also, you can play with the CurvedAnimation
new CurvedAnimation(
parent: routeAnimation,
curve: Curves.elasticIn,
)
CustomRoute.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
final Tween<Offset> _kBottomUpTween = new Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: Offset.zero,
);
// Offset from offscreen to the right to fully on screen.
final Tween<Offset> _kRightMiddleTween = new Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
);
// Offset from offscreen below to fully on screen.
class AppPageRoute extends MaterialPageRoute<String> {
#override
final bool maintainState;
#override
final WidgetBuilder builder;
CupertinoPageRoute<String> _internalCupertinoPageRoute;
AppPageRoute({
#required this.builder,
RouteSettings settings: const RouteSettings(),
this.maintainState: true,
bool fullscreenDialog: false,
}) : assert(builder != null),
assert(settings != null),
assert(maintainState != null),
assert(fullscreenDialog != null),
super(
settings: settings,
fullscreenDialog: fullscreenDialog,
builder: builder,
) {
assert(opaque); // PageRoute makes it return true.
}
#override
Color get barrierColor => null;
#override
Duration get transitionDuration => const Duration(milliseconds: 1000);
CupertinoPageRoute<String> get _cupertinoPageRoute {
assert(_useCupertinoTransitions);
_internalCupertinoPageRoute ??= new CupertinoPageRoute<String>(
builder: builder,
fullscreenDialog: fullscreenDialog,
hostRoute: this,
);
return _internalCupertinoPageRoute;
}
bool get _useCupertinoTransitions {
return _internalCupertinoPageRoute?.popGestureInProgress == true ||
Theme.of(navigator.context).platform == TargetPlatform.iOS;
}
#override
Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
final Widget result = builder(context);
assert(() {
if (result == null) {
throw new FlutterError('The builder for route "${settings.name}" returned null.\n'
'Route builders must never return null.');
}
return true;
}());
return result;
}
#override
Widget buildTransitions(
BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
if (_useCupertinoTransitions) {
return _cupertinoPageRoute.buildTransitions(context, animation, secondaryAnimation, child);
}
return new _CustomPageTransition(routeAnimation: animation, child: child, fullscreenDialog: fullscreenDialog);
}
}
class _CustomPageTransition extends StatelessWidget {
final Animation<Offset> _positionAnimation;
final Widget child;
final bool fullscreenDialog;
_CustomPageTransition({
Key key,
#required Animation<double> routeAnimation,
#required this.child,
#required this.fullscreenDialog,
}) : _positionAnimation = !fullscreenDialog
? _kRightMiddleTween.animate(new CurvedAnimation(
parent: routeAnimation,
curve: Curves.elasticIn,
))
: _kBottomUpTween.animate(new CurvedAnimation(
parent: routeAnimation, // The route's linear 0.0 - 1.0 animation.
curve: Curves.elasticIn,
)),
super(key: key);
#override
Widget build(BuildContext context) {
return new SlideTransition(
position: _positionAnimation,
child: child,
);
}
}
Push new Route
Future goToHomePage() async {
await new Future.delayed(const Duration(milliseconds: 4000));
Navigator.of(context).push(new AppPageRoute(builder: (BuildContext context) => new HomePage()));
}
You can use Custom MaterialPageRoute for the splash screen and for other routes MaterialPageRoute.
Hope it helps
// 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.
Hero animation is quite easy to implement in Flutter. You just need to import the package package:flutter/scheduler.dart in your landing page. See below code
import 'package:flutter/scheduler.dart' show timeDilation;
Remember to add timeDilation variable and assign it a value to speed up or slow down the animation in seconds. Add these just after your build method as in example below
Widget build(BuildContext context) {
timeDilation = 2;
For better animation, just use a custom animation