Hero animation is not working when navigating to new page flutter - flutter

I have product items in grid view which is a future builder, and wrapped with Hero Widget and gave a unique tag by id, and in detail new page also I wrapped with Hero Widget and gave same unique tag but the animation is working only when coming back to screen. I didn't understand why Hero animation is not working when navigating to a new page, maybe because of Future builder? or I made any mistake? don't know what happening, Can anyone Help me to achieve nice Hero animation. Below I provided my code. Please feel free to ask any questions. Thanks in advance.
main.dart
import 'package:flutter/material.dart';
import 'package:httprequest/screens/all_products.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const AllProductsScreen(),
);
}
}
all_products.dart
import 'package:flutter/material.dart';
import 'package:httprequest/screens/single_product.dart';
import 'package:httprequest/services/api_services.dart';
class AllProductsScreen extends StatefulWidget {
const AllProductsScreen({Key? key}) : super(key: key);
#override
_AllProductsScreenState createState() => _AllProductsScreenState();
}
class _AllProductsScreenState extends State<AllProductsScreen> {
Future ? products;
#override
void initState() {
// TODO: implement initState
products = ApiServices().getAllProducts();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Products"),
),
body: Container(
padding: EdgeInsets.all(8.0),
child: FutureBuilder(
future: products,
builder: (context, AsyncSnapshot snapshot){
if(snapshot.hasData){
return Center(
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200,
childAspectRatio: 2 / 3,
crossAxisSpacing: 20,
mainAxisSpacing: 20),
itemCount: snapshot.data.length,
itemBuilder: (BuildContext ctx, index) {
return GestureDetector(
child: Hero(
tag: snapshot.data[index]["id"],
child: Card(
child: Container(
padding: EdgeInsets.all(5.0),
child: Column(
children: [
Image.network(snapshot.data[index]["image"],height: 180,width: 180,),
Text(snapshot.data[index]["title"],textAlign: TextAlign.center,maxLines: 2,overflow: TextOverflow.ellipsis,),
Text("\$: ${snapshot.data[index]["price"]}")
],
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15)),
),
),
),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => SingleProduct(snapshot.data[index]["id"])));
},
);
}),
);
}
return const Center(child: CircularProgressIndicator(),);
},
),
),
);
}
}
single_product.dart
import 'package:flutter/material.dart';
import 'package:httprequest/services/api_services.dart';
class SingleProduct extends StatefulWidget {
final id;
SingleProduct(this.id);
#override
_SingleProductState createState() => _SingleProductState();
}
class _SingleProductState extends State<SingleProduct> {
Future ? product;
#override
void initState() {
// TODO: implement initState
product = ApiServices().getSingleProduct(widget.id);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Products"),
),
body: FutureBuilder(
future: product,
builder: (context,AsyncSnapshot snapshot){
if(snapshot.hasData){
return Container(
color: Colors.white,
child: Column(
children: [
Hero(
tag: widget.id,
child: Container(
color: Colors.transparent,
child: Center(
child: Image.network(snapshot.data["image"],height: 200,width: 200,),
),
),
),
Expanded(
child: Container(
color: Colors.transparent,
child: Card(
color: Colors.white,
elevation: 20.0,
shape: RoundedRectangleBorder(
//side: BorderSide(width: 0.2),
borderRadius: BorderRadius.only(topRight: Radius.circular(20),topLeft: Radius.circular(20))),
child: Container(
padding: EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(snapshot.data["title"],textAlign: TextAlign.center,style: TextStyle(fontWeight: FontWeight.bold,fontSize: 16),),
SizedBox(height: 5,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("\$: ${snapshot.data["price"]}",style: TextStyle(fontSize: 16),),
Row(
children: [
Text(snapshot.data["rating"]["rate"].toString(),style: TextStyle(fontSize: 16),),
Icon(Icons.star,color: Colors.yellow,size: 20,),
],
),
],
),
SizedBox(height: 5,),
Text(("Category: ${snapshot.data["category"]}"),textAlign: TextAlign.left,style: TextStyle(fontSize: 16),),
SizedBox(height: 5,),
Text(snapshot.data["description"],textAlign: TextAlign.justify,style: TextStyle(fontSize: 16),),
],
),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(50),
color: Colors.black,
),
height: 50,
width: 130,
//color: Colors.black,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(Icons.shopping_cart,color: Colors.white,),
Text("Add to cart",style: TextStyle(color: Colors.white),),
],
),
),
),
],
),
),
),
),
),
],
),
);
}
return Center(child: CircularProgressIndicator());
},
),
);
}
}
api_services.dart
import 'dart:developer';
import 'dart:convert';
import 'package:http/http.dart' as http;
class ApiServices {
Future getAllProducts() async {
var allProcuctsUri = Uri.parse('https://fakestoreapi.com/products');
var response = await http.get(allProcuctsUri);
log("All Products response : ${response.statusCode.toString()}");
log("All Products body : ${response.body}");
return json.decode(response.body);
}
Future getSingleProduct(int id) async {
var singleProcuctUri = Uri.parse('https://fakestoreapi.com/products/${id}');
var response = await http.get(singleProcuctUri);
log("Single Product response : ${response.statusCode.toString()}");
log("Single Product body : ${response.body}");
return json.decode(response.body);
}
}

you have to provide the same tag in hero widget to both the screens which you want to animate. you have wrap the widget which to animate with HeroAnimation and provide tag and then wrap the other screen with HeroAnimation and provide the same tag to both the HeroAnimation widgets..

to check whether two tags are same first print snapshot.data[index]["id"] of all_products.dart and widget.id of single_product.dart.
if the second page tag is getting null, please initialize a variable inside build of single_product.dart like
#override
String finalid=widget.id; //add this and get reference from this
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Products"),
),
it would be better if both tags are in String format.

Related

Sending photo to GridView from preview page

When the user agrees to the photo, I want to send the photo back to the homepage where they can access the photo later on.
Currently, I am just opening the camera again on the PhotoPreview page when the user clicks the second button (OutlineButton). Instead, I want this photo to be sent to the homepage.
Here is the relevant portion of the PhotoPreview page
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () => Navigator.pop(
context), // Go back to the camera to take the picture again
child: Icon(Icons.camera_alt),
),
appBar: AppBar(title: Text('Photo Preview')),
body: Column(children: [
Expanded(child: Image.file(File(widget.imagePath))),
const SizedBox(height: 16.0),
OutlineButton(
onPressed: () {
_openGallery();
Navigator.pop(context);
},
child: Text('Okay'),
borderSide: BorderSide(color: Color(0xff33333D)),
),
]),
);
}
}
The Gridview on my home page, which renders the photo in the format I want, is as such
: GridView.count(
shrinkWrap: true,
crossAxisCount: 2,
crossAxisSpacing: 25,
mainAxisSpacing: 25,
childAspectRatio: (80 / 150),
padding: const EdgeInsets.all(2.0),
children:
List.generate(widget.imageArray.length, (index) {
return Container(
decoration: new BoxDecoration(
color: const Color(0xff000000),
borderRadius: BorderRadius.circular(10),
image: new DecorationImage(
image: FileImage(widget.imageArray[index]),
fit: BoxFit.fill,
colorFilter: new ColorFilter.mode(
Colors.black.withOpacity(0.4),
BlendMode.dstATop),
How can I connect the two, for when the user clicks the OutlineButton that it sends the photo on the preview page to the home screen in the format above?
Edit per answer: Here is full Homepage
import 'dart:io';
import 'package:flutter/material.dart';
class Homepage_1 extends StatefulWidget {
final List<File> imageArray;
Homepage_1({Key key, this.imageArray}) : super(key: key);
#override
_Homepage_1State createState() => _Homepage_1State();
}
class _Homepage_1State extends State<Homepage_1> {
var image;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Colors.white,
body: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
Padding(
padding:
const EdgeInsets.only(top: 100, left: 40, right: 0, bottom: 0),
child:
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(
'App Name',
style: TextStyle(
fontSize: 60,
fontFamily: 'Avenir',
fontWeight: FontWeight.w900,
),
),
Container(
margin:
EdgeInsets.only(top: 0, left: 0, right: 50, bottom: 0),
child: widget.imageArray.isEmpty
? Column(children: [
Text(
'Yikes! You have no photos',
style: TextStyle(
fontSize: 19,
fontFamily: 'Avenir',
fontWeight: FontWeight.w900,
),
),
Text(
'Click the circular button below'
style: TextStyle(
fontSize: 15,
fontFamily: 'Avenir',
fontWeight: FontWeight.w500,
),
),
])
: GridView.count(
shrinkWrap: true,
crossAxisCount: 2,
crossAxisSpacing: 25,
mainAxisSpacing: 25,
childAspectRatio: (80 / 150),
padding: const EdgeInsets.all(2.0),
children:
List.generate(widget.imageArray.length, (index) {
return Container(
decoration: new BoxDecoration(
color: const Color(0xff000000),
borderRadius: BorderRadius.circular(10),
image: new DecorationImage(
image: FileImage(widget.imageArray[index]),
fit: BoxFit.fill,
colorFilter: new ColorFilter.mode(
Colors.black.withOpacity(0.4),
BlendMode.dstATop),
),
),
);
})))
]),
)
]));
}
}
& here is full Photo preview screen:
import 'package:flutter/material.dart';
import 'dart:io';
class PhotoPreviewScreen extends StatefulWidget {
Function setData;
final String imagePath;
PhotoPreviewScreen({Key key, this.setData, this.imagePath}) : super(key: key);
_PhotoPreviewScreenState createState() => _PhotoPreviewScreenState();
}
class _PhotoPreviewScreenState extends State<PhotoPreviewScreen> {
var image;
Future _openGallery() async {
if (widget.setData != null) {
widget.setData(File(image.path));
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () => Navigator.pop(
context), // Go back to the camera to take the picture again
child: Icon(Icons.camera_alt),
),
appBar: AppBar(title: Text('Photo Preview')),
body: Column(children: [
Expanded(child: Image.file(File(widget.imagePath))),
const SizedBox(height: 16.0),
OutlineButton(
onPressed: () async {
await _openGallery();
Navigator.of(context).pop(widget.imagePath);
},
child: Text('Okay'),
borderSide: BorderSide(color: Color(0xff33333D)),
),
]),
);
}
}
You can pass arguments to pop method and received that as a return value of push .
I wrote a minimal sample for you. Hopefully you get the idea, but if you have any questions, please don't hesitate to ask!
class PhotoPreviewPage extends StatefulWidget {
const PhotoPreviewPage({Key? key, #required this.imagePath})
: super(key: key);
#override
_PhotoPreviewPageState createState() => _PhotoPreviewPageState();
final String imagePath;
}
class _PhotoPreviewPageState extends State<PhotoPreviewPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: OutlineButton(
onPressed: () {
Navigator.of(context).pop(widget.imagePath);
},
child: const Text('OK'),
),
),
);
}
}
/// This is a overly simplified version of the CameraPage
/// Basically, you take a photo and pass that to cameraPreview page
class CameraPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FlatButton(
onPressed: () async {
final imagePath = await _takeAPhoto();
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => PhotoPreviewPage(imagePath: imagePath),
),
);
},
child: Text('take a photo'),
),
),
);
}
Future<String> _takeAPhoto() {
// some logic to take a photo and return imagePath
return imagePath;
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
final List<File> imageArray = [];
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: GridView.count(
crossAxisCount: 2,
children: List.generate(
widget.imageArray.length,
(index) => Container(
decoration: BoxDecoration(
image: DecorationImage(
image: FileImage(widget.imageArray[index]),
fit: BoxFit.fill,
),
),
),
),
),
bottomNavigationBar: Row(
children: [
IconButton(
onPressed: () async {
final filePath = await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => PhotoPreviewPage(),
),
);
if (filePath != null) {
setState(() {
widget.imageArray.add(File(filePath));
});
}
},
icon: Icon(Icons.camera),
),
],
),
);
}
}
But depending on the exact page structure of your app, you might need to look into state management solutions like Bloc or Riverpod.

Image Gallery in flutter from assets

I want to load all the images from assets folder in my flutter app Select Picture screen. And when the user selects and image it will take half space in another screen. So it's very similar to the regular edit image functionality in our phone.
This is what I want after the user has selected an image.
I've successfully added all the images to a screen called gallery:
And this is how I did it:
import 'package:flutter/material.dart';
import 'package:flutter_app/src/components/ImageDetails.dart';
List<ImageDetails> _images = [
ImageDetails(
imagePath: 'assets/images/hut.png',
title: 'Hutt',
),
ImageDetails(
imagePath: 'assets/images/scenary.png',
title: 'Scenary',
),
ImageDetails(
imagePath: 'assets/images/menu.png',
title: 'Menu Bar',
),
];
class ImageSelection extends StatefulWidget {
#override
_ImageSelectionState createState() => _ImageSelectionState();
}
class _ImageSelectionState extends State<ImageSelection> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.lightBlueAccent,
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
SizedBox(
height: 40,
),
Text(
'Gallery',
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.w600,
color: Colors.white,
),
textAlign: TextAlign.center,
),
SizedBox(
height: 40,
),
Expanded(
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 20,
vertical: 30,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
),
),
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemBuilder: (context, index) {
return RawMaterialButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailsPage(
imagePath: _images[index].imagePath,
title: _images[index].title,
index: index,
),
),
);
},
child: Hero(
tag: 'logo$index',
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
image: DecorationImage(
image: AssetImage(_images[index].imagePath),
fit: BoxFit.cover,
),
),
),
),
);
},
itemCount: _images.length,
),
),
)
],
),
),
);
}
}
class ImageDetails {
final String imagePath;
final String title;
ImageDetails({
#required this.imagePath,
#required this.title,
});
}
But I want to do this dynamically so if I add a new image in assets the application will automatically show the images. And on select the image will take below shown space in the canvas page?
import 'dart:collection';
import 'package:flutter/material.dart';
import 'dart:io';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_image_gallery/flutter_image_gallery.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
Map<dynamic, dynamic> allImageInfo = new HashMap();
List allImage = new List();
#override
void initState() {
super.initState();
loadImageList();
}
Future<void> loadImageList() async {
Map<dynamic, dynamic> allImageTemp;
allImageTemp = await FlutterImageGallery.getAllImages;
print(" call $allImageTemp.length");
setState(() {
this.allImage = allImageTemp['URIList'] as List;
});
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
debugShowCheckedModeBanner: false,
home: new Scaffold(
appBar: new AppBar(
title: const Text('Image Gallery'),
),
body: _buildGrid(),
),
);
}
Widget _buildGrid() {
return GridView.extent(
maxCrossAxisExtent: 150.0,
padding: const EdgeInsets.all(4.0),
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
children: _buildGridTileList(allImage.length));
}
List<Container> _buildGridTileList(int count) {
return List<Container>.generate(
count,
(int index) => Container(
child: new Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Image.file(
File(allImage[index].toString()),
width: 96.0,
height: 96.0,
fit: BoxFit.contain,
),
],
)));
}
}
Dont Forget to Import :
dependencies:
flutter_image_gallery: ^1.0.6

Navigate the page

I am new to Flutter. I know how to navigate from a page to another page, but the problem is, my navigation does not work. In my short experience, I realized that you cannot navigate from the stateless to the stateful widget or the opposite one.
I will share my code below. So could you please tell me what kind of mistake I am making?
Thank you. :)
Actually I was planning to remove the button because this page could be the intro page like it could show the image and text I want and after 3 seconds it could go to the MainApp() page. Maybe if you have any idea how to do it. I would like to hear that too.:)
void main() {
runApp(Intro());
}
class Intro extends StatefulWidget {
#override
_IntroState createState() => _IntroState();
}
class _IntroState extends State<Intro> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.yellow.shade300,
body: SafeArea(
child: (Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Image.asset(
'assets/dinoapp2.png',
height: 200.0,
width: 200.0,
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(top: 40.0),
child: Text(
'Big Title',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Russo One Regular',
fontSize: 30.0,
color: Colors.blueAccent),
),
),
),
Container(
margin: EdgeInsets.fromLTRB(120.0, 20, 120, 100),
child: RaisedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MainApp()),
);
},
padding: EdgeInsets.all(20.0),
textColor: Colors.black,
color: Colors.white,
child: Text(
"Start",
style: TextStyle(fontSize: 20.0),
),
),
)
],
)),
),
),
);
}
}
class MainApp extends StatefulWidget {
#override
_MainAppState createState() => _MainAppState();
}
class _MainAppState extends State<MainApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.blue,
body: SafeArea(
child: Center(
child: Text('This page I want to go after I press Start button'),
),
),
),
);
}
}
Try This
void main() {
runApp(MaterialApp(
home: Intro(),
));
}
class Intro extends StatefulWidget {
#override
_IntroState createState() => _IntroState();
}
class _IntroState extends State<Intro> {
#override
void initState() {
Future.delayed(Duration(seconds: 3), () {
Route route = MaterialPageRoute(builder: (context) => MainApp());
Navigator.push(context, route);
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.yellow.shade300,
body: SafeArea(
child: (Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Image.asset(
'assets/dinoapp2.png',
height: 200.0,
width: 200.0,
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(top: 40.0),
child: Text(
'Big Title',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Russo One Regular',
fontSize: 30.0,
color: Colors.blueAccent),
),
),
),
Center(
child: CircularProgressIndicator(),
)
],
)),
),
);
}
}
class MainApp extends StatefulWidget {
#override
_MainAppState createState() => _MainAppState();
}
class _MainAppState extends State<MainApp> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blue,
body: SafeArea(
child: Center(
child: Text('This page I want to go after I press Start button'),
),
),
);
}
}
Just Add this function in your intro class
#override
void initState() {
super.initState();
Future.delayed(const Duration(seconds: 2), () {
Navigator.push( context, MaterialPageRoute(builder: (context) =>
MainApp()),
); });
}
I don't know why but I copied your code into Intro class. It didn't work. :(

How to center AppBar and how to reduce leading icon size in PrefrerredSize?

How to center whole AppBar and how to reduce leading icon?
I tried with Center widget and Column with mainAxisAlignment.center not working.
And I tried add width and height to leading icon container.but nothing is working
appBar: PreferredSize(
child: AppBar(
leading: Container(
decoration: BoxDecoration(..),
child: Icon(..),
),
title: TextFormField(
...
),
actions: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
...
),
CupertinoSwitch(
...
)
],
)
],
),
preferredSize: Size.fromHeight(80.0)),
As shown here.
as an option
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AwesomeAppBar(height: 80),
),
);
}
}
class AwesomeAppBar extends PreferredSize {
final double height;
const AwesomeAppBar({Key key, #required this.height});
#override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, snapshot) {
return Container(
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
height: height,
color: Theme.of(context).primaryColor,
child: Row(
children: <Widget>[
Container(
padding: EdgeInsets.all(16),
child: Icon(
Icons.arrow_back,
color: Colors.white,
),
),
Expanded(
child: Container(
height: 32,
alignment: Alignment.centerLeft,
padding: EdgeInsets.symmetric(horizontal: 16),
margin: EdgeInsets.only(right: 16),
decoration: ShapeDecoration(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
color: Colors.white,
),
child: Text('Search'),
),
),
SwitchWithText(),
SizedBox(width: 16),
],
),
);
});
}
#override
Size get preferredSize => Size.fromHeight(height);
}
class SwitchWithText extends StatefulWidget {
#override
_SwitchWithTextState createState() => _SwitchWithTextState();
}
class _SwitchWithTextState extends State<SwitchWithText> {
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Text('Online', style: TextStyle(color: Colors.white)),
CupertinoSwitch(
value: true,
onChanged: (b) {},
activeColor: Colors.lightBlueAccent,
),
],
);
}
}

Flutter Listview.builder with http Request refreshes unwontedly when I navigate to other pages and go back

I have this list view set up in a tab bar, when I navigate to other tabs and go back, the list view refreshes and sends a new http request to the database, how do I fix this?
import 'package:flutter/material.dart';
import 'main.dart';
import 'individual_page.dart';
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
class FriendsPage extends StatefulWidget {
#override
FriendsPageState createState() => FriendsPageState();
}
class FriendsPageState extends State<FriendsPage> {
Map data;
List userData;
Future getFriends() async {
http.Response response =
await http.get("https://reqres.in/api/users?page=2");
debugPrint(response.body);
data = json.decode(response.body);
setState(() {
userData = data["data"];
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
getFriends();
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {},
backgroundColor: Colors.pink,
child: Icon(
Icons.add_circle,
size: 30,
color: Colors.white,
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.endDocked,
bottomNavigationBar: BottomAppBar(
shape: CircularNotchedRectangle(),
color: Colors.pink,
child: Container(
height: 30,
child: Container(
margin: EdgeInsets.all(15),
child: Center(
child: Text(
"Social}",
style: TextStyle(color: Colors.white, fontSize: 20),
),
),
),
),
),
body: Container(
child: ListView.builder(
itemCount: userData == null ? 0 : userData.length,
itemBuilder: (BuildContext context, int index) {
return Card(
elevation: 0,
margin: EdgeInsets.all(5),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
children: <Widget>[
CircleAvatar(
backgroundImage: NetworkImage(userData[index]. ["avatar"]),
),
Container(
padding: EdgeInsets.all(18),
child: Text(
"${userData[index]["first_name"]} "
"${userData[index]["last_name"]}",
style: TextStyle(fontSize: 14),
),
)
],
),
),
);
},
)),
);
}
}
I want the ListView to load once and then stay loaded in the background, I don't want it to reload every time I navigate pages!