Flutter expandable tiles inside grid view

6.6k views Asked by At

So this is what I'm trying to acheive

I'm trying to achieve this functionality on flutter but honestly have no idea how to do it. I've been trying to figure it out for weeks, I tried flutter_staggered_grid_view, which was kind of the closest to this, but that didn't help either. Does anyone have any idea on how to achieve this effect?

3

There are 3 answers

0
Try On BEST ANSWER

You can use the Wrap widget as grid, and use some custom widget with AnimatedContainer to expand and retract the bloc.

//number of childs used in the example
static const itemCount = 8;

//list of each bloc expandable state, that is changed to trigger the animation of the AnimatedContainer 
List<bool> expandableState = List.generate(itemCount, (index) => false);

Widget bloc (double width, int index) {
  bool isExpanded = expandableState[index];

  return GestureDetector(
    onTap: () {
      setState(() {
        //changing the current expandableState
        expandableState[index] = !isExpanded;
      });
    },
    child: AnimatedContainer(
      duration: Duration(milliseconds: 200),
      margin: const EdgeInsets.all(20.0),
      width: !isExpanded ? width * 0.4 : width * 0.8,
      height: !isExpanded ? width * 0.4 : width * 0.8,
      color: Colors.red,
    ),
  );
}

@override
Widget build(BuildContext context) {
  double width = MediaQuery.of(context).size.width;

  return Scaffold(
    body: Align(
      child: SingleChildScrollView(
        child: Wrap(
          children: List.generate(itemCount, (index) {
            return bloc(width, index);
          }),
        ),
      ),
    ),
  );
}
0
bluenile On

You can use Wrap widget to achieve the result. Please see the code below.

import 'package:flutter/material.dart';

final Color darkBlue = const Color.fromARGB(255, 18, 32, 47);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(title: const Text("Demo")),
        body: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  final int _cells = 8;
  final double _containerSizeSmall = 75;
  final double _containerSizeLarge = 170;
  final double _padding = 10;
  int _clicked = 0;
  @override
  Widget build(BuildContext context) {
    final Size size = MediaQuery.of(context).size;
    return SingleChildScrollView(
      child: Container(
        height: size.height,
        width: 240,
        child: Wrap(
          children: List.generate(
            _cells,
            (col) => Padding(
              padding: EdgeInsets.all(_padding),
              child: GestureDetector(
                onTap: () {
                  setState(() {
                    _clicked != col + 1 ? _clicked = col + 1 : _clicked = 0;
                  });
                },
                child: Container(
                  height: _clicked == col + 1
                      ? _containerSizeLarge
                      : _containerSizeSmall,
                  width: _clicked == col + 1
                      ? _containerSizeLarge
                      : _containerSizeSmall,
                  decoration: const BoxDecoration(
                    color: Colors.blue,
                    borderRadius: const BorderRadius.all(
                      const Radius.circular(5),
                    ),
                  ),
                  child: Center(child: Text('${col + 1}')),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}
0
3li On

You could have a list of the items that should be expanded and lay them out in the grid view accordingly (using flutter_staggered_grid_view for example).

I edited the example written in the library docs to achieve the following result:

Expandable GridView Demo

Basically,

  1. create a StatefulWidget and add a list to it (_expandedIndices). The purpose of the list is to keep track of the indices of the items that are expanded.

  2. add a GestureDetector on the grid cells to detect taps and add/remove indices from the list (add the index to the list if it is not already there, otherwise remove it).

Don't forget to put the code that updates the list inside a setState.

  1. in the staggeredTileBuilder configure crossAxisCellCount and mainAxisCellCount based on whether the item in index should be expanded or not.
class StaggeredGridViewWithExpandableCells extends StatefulWidget {
  @override
  _StaggeredGridViewWithExpandableCellsState createState() =>
      _StaggeredGridViewWithExpandableCellsState();
}

class _StaggeredGridViewWithExpandableCellsState
    extends State<StaggeredGridViewWithExpandableCells> {
  final _expandedIndices = Set<int>();

  @override
  Widget build(BuildContext context) {
    return StaggeredGridView.countBuilder(
      crossAxisCount: 4,
      itemCount: 16,
      itemBuilder: (BuildContext context, int index) => GestureDetector(
        onTap: () => setState(() => _expandedIndices.contains(index) ? _expandedIndices.remove(index) : _expandedIndices.add(index)),
        child: new Container(
            color: Colors.green,
            child: new Center(
              child: new CircleAvatar(
                backgroundColor: Colors.white,
                child: new Text('$index'),
              ),
            )),
      ),
      staggeredTileBuilder: (int index) =>
          new StaggeredTile.count(_expandedIndices.contains(index) ? 4 : 2, 1),
      mainAxisSpacing: 4.0,
      crossAxisSpacing: 4.0,
    );
  }
}