Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GestureDetector is untouchable in staggered GridView with OverflowBox

I use GridView to display a 2-column list of products. The right column will be pushed down a bit in order to make a staggered grid view.

To do that, I use OverflowBox for products in the right column. I get the desired UI. Each grid tile is wrapped by GestureDetector which just prints 'Tapped' when the user touches a grid tile. However, the pushed part of the grid tile is untouchable (the red box in the image).

Here is my code snippet and screenshots for illustration:

import 'package:flutter/material.dart';

import '../models/products.dart';
import './vertical_product_card.dart';

class ProductsGrid extends StatelessWidget {
  final List<Product> products;

  ProductsGrid(this.products);

  @override
  Widget build(BuildContext context) {
    final screenWidth = MediaQuery.of(context).size.width;
    return ClipRRect(
      borderRadius: BorderRadius.only(
        bottomLeft: Radius.circular(40),
        bottomRight: Radius.circular(40),
      ),
      child: Container(
        color: Theme.of(context).primaryColor,
        child: GridView.builder(
          shrinkWrap: true,
          padding:
              const EdgeInsets.only(top: 10, bottom: 50, left: 15, right: 15),
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 2,
            childAspectRatio: (screenWidth - 30 - 15) / (2 * 290),
            mainAxisSpacing: 15,
            crossAxisSpacing: 15,
            // Product Card has fixed height 290, screenWidth must substract the total horizontal padding of GridView
          ),
          itemCount: products.length,
          itemBuilder: (_, i) {
            if (i % 2 == 0) {
              return GestureDetector(
                onTap: () {
                  print('Tapped');
                },
                child: VerticalProductCard(products[i]),
              );
            }
            return GestureDetector(
              onTap: () {
                print('Tapped');
              },
              child: OverflowBox(
                maxHeight: 290.0 + 70.0,
                child: Container(
                  margin: EdgeInsets.only(top: 70),
                  child: VerticalProductCard(products[i]),
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}

The part inside red box is untouchable

like image 326
Erron Developer Avatar asked Nov 07 '22 06:11

Erron Developer


1 Answers

You can create Scrollable Widget with Row of two ViewPorts

enter image description here

import 'package:flutter/material.dart';

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primaryColor: Colors.orange[50],
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('All products'),
        ),
        body: SafeArea(
          child: MyHomePage(),
        ),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return ProductsGrid([
      Product(
        manufacturer: 'Cadbury',
        title: 'Dairy Milk Chocolate Freddo',
        perHundredGramm: 143,
        price: 0.5,
      ),
      Product(
        manufacturer: 'Cadbury',
        title: 'Caranello Koala Chocolate',
        perHundredGramm: 143,
        price: 0.5,
      ),
      Product(
        manufacturer: 'Mama',
        title: 'Shrimp Tom Yam Noodles Jumbo Pack',
        perHundredGramm: 143,
        price: 0.5,
      ),
      Product(
        manufacturer: 'Mama',
        title: 'Pork Noodle Jumbo Pack',
        perHundredGramm: 0.62,
        price: 0.72,
      ),
      Product(
        manufacturer: 'Cadbury',
        title: 'Dairy Milk Chocolate Freddo',
        perHundredGramm: 0.62,
        price: 0.72,
      ),
      Product(
        manufacturer: 'Someone',
        title: 'Something',
        perHundredGramm: 0.1,
        price: 1,
      ),
      Product(
        manufacturer: 'Someone',
        title: 'Something',
        perHundredGramm: 0.1,
        price: 1,
      ),
      Product(
        manufacturer: 'Someone',
        title: 'Something',
        perHundredGramm: 0.1,
        price: 1,
      ),
      Product(
        manufacturer: 'Someone',
        title: 'Something',
        perHundredGramm: 0.1,
        price: 1,
      ),
      Product(
        manufacturer: 'Someone',
        title: 'Something',
        perHundredGramm: 0.1,
        price: 1,
      )
    ]);
  }
}

class ProductsGrid extends StatelessWidget {
  ProductsGrid(this.products);

  final List<Product> products;

  @override
  Widget build(BuildContext context) {
    var oddList = <Product>[];
    var evenList = <Product>[];
    products.asMap().forEach((key, value) {
      key.isEven ? evenList.add(value) : oddList.add(value);
    });
    return ClipRRect(
        borderRadius: BorderRadius.only(
          bottomLeft: Radius.circular(40),
          bottomRight: Radius.circular(40),
        ),
        child: Scrollable(
          physics: ClampingScrollPhysics(),
          viewportBuilder: (_, viewportOffset) => Container(
            color: Theme.of(context).primaryColor,
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Flexible(
                  flex: 1,
                  child: Viewport(
                    axisDirection: AxisDirection.down,
                    offset: viewportOffset,
                    slivers: oddList
                        .map((el) => SliverToBoxAdapter(
                              child: VerticalProductCard(el),
                            ))
                        .toList(),
                  ),
                ),
                Flexible(
                  flex: 1,
                  child: Viewport(
                    axisDirection: AxisDirection.down,
                    offset: viewportOffset,
                    slivers: evenList
                        .asMap()
                        .map(
                          (key, el) => MapEntry(
                            key,
                            SliverToBoxAdapter(
                              child: Padding(
                                padding:
                                    EdgeInsets.only(top: key == 0 ? 70 : 0),
                                child: VerticalProductCard(el),
                              ),
                            ),
                          ),
                        )
                        .values
                        .toList(),
                  ),
                ),
              ],
            ),
          ),
        ));
  }
}

@immutable
class Product {
  final double price;
  final String manufacturer;
  final String title;
  final double perHundredGramm;

  Product({
    @required this.price,
    @required this.manufacturer,
    @required this.title,
    @required this.perHundredGramm,
  });
}

class VerticalProductCard extends StatefulWidget {
  const VerticalProductCard(this.product, {Key key}) : super(key: key);

  final Product product;

  @override
  _VerticalProductCardState createState() => _VerticalProductCardState();
}

class _VerticalProductCardState extends State<VerticalProductCard> {
  bool isFavorite = false;

  _onTap() {
    setState(() {
      isFavorite = !isFavorite;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
      ),
      padding: EdgeInsets.all(10),
      margin: EdgeInsets.all(10),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          Text(
            'coles',
            style: TextStyle(color: Colors.red),
            textAlign: TextAlign.center,
          ),
          Center(
            child: Container(
              width: 50,
              child: Placeholder(
                fallbackHeight: 50,
                fallbackWidth: 30,
              ),
            ),
          ),
          Text('\$ ${widget.product.price}'),
          Text(widget.product.manufacturer),
          Text(widget.product.title),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: <Widget>[
              Text(
                '\$${widget.product.perHundredGramm} per 100G',
                style: TextStyle(fontSize: 12),
              ),
              IconButton(
                icon: Icon(
                  Icons.favorite,
                  color: isFavorite ? Colors.redAccent : Colors.grey,
                ),
                onPressed: _onTap,
              )
            ],
          )
        ],
      ),
    );
  }
}
like image 163
Kherel Avatar answered Dec 02 '22 18:12

Kherel