2

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);
  }
}
shanmkha
  • 416
  • 1
  • 8
  • 27
  • I'm not all too knowledgeable on transitions but I think the child of the hero widget needs to be the same. So in single_product.dart try wrapping the Container widget inside the Hero with a Card widget. – darkstar Dec 29 '21 at 22:28
  • @darkstar but I did it already, I wrapped the container with Hero widget. If I was mistaken can you provide a working code, that will be really helpfull for me. – shanmkha Dec 30 '21 at 10:03

2 Answers2

0

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..

Nitts
  • 26
  • 5
0

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.

mayur acharya
  • 21
  • 1
  • 8