Draw outside listview bounds in Flutter

Viewed 1852

I want to transform my item that it is bigger than the listview itself. (intention for focused navigation)

My List:

        Container(
          height: 100,
          child: ListView.builder(
            itemBuilder: (context, index) => HomeItem(title: '$index'),
            scrollDirection: Axis.horizontal,
          ),
        ),

My Item:

class HomeItem extends StatelessWidget {
  final String title;
  final bool expand;

  const HomeItem({
    @required this.title,
    this.expand = false,
  });

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: ThemeDimens.padding8),
      child: Transform.scale(
        scale: expand ? 1.5 : 1,
        child: AnimatedContainer(
          width: 50,
          height: 100,
          color: expand ? ThemeColors.accent : ThemeColors.primary,
          duration: ThemeDurations.shortAnimationDuration(),
          child: Center(
            child: Text(title),
          ),
        ),
      ),
    );
  }
}

Current behaviour enter image description here

Expected behaviour enter image description here

1 Answers

If you try to use OverflowBox or Transform, content of an item will still clip and won't be drawn outside of its bounding box. But it's possible to use Overlay to draw an element on top of list and position it on a specific list item, though it's a bit complicated.

demo

class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
  final elements = List.generate(12, (i) => i);

  int selectedIndex;
  OverlayEntry overlayEntry;
  List<LayerLink> layerLinks;

  @override
  void initState() {
    super.initState();

    // Creating a layer link for each list cell
    layerLinks = List.generate(elements.length, (i) => LayerLink());
  }

  void createOverlayEntry(int i, BuildContext context) {
    // Removing an overlay entry, if there was one
    overlayEntry?.remove();

    final renderBox = context.findRenderObject() as RenderBox;
    final size = renderBox.size;
    final offset = renderBox.localToGlobal(Offset.zero);

    // Creating a new overlay entry linked to specific list element
    overlayEntry = OverlayEntry(
      builder: (context) => Positioned(
        left: 0,
        top: 0,
        child: CompositedTransformFollower(
          link: layerLinks[i],
          showWhenUnlinked: false,
          offset: Offset(-20, 0),
          child: Material(
            color: Colors.yellow,
            child: InkWell(
              onTap: () {
                setState(() {
                  selectedIndex = null;
                });

                overlayEntry?.remove();
                overlayEntry = null;
              },
              child: Container(
                alignment: Alignment.center,
                width: 70,
                height: elementHeight,
                child: Text('$i')
              ),
            )
          ),
        )
      )
    );

    // Inserting an entry
    Overlay.of(context).insert(overlayEntry);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Container(
        height: elementHeight,
        child: ListView.separated(
          scrollDirection: Axis.horizontal,
          itemCount: elements.length,
          itemBuilder: (c, i) {
            return CompositedTransformTarget(
              link: layerLinks[i],
              child: Material(
                color: Colors.red,
                child: InkWell(
                  onTap: () {
                    setState(() {
                      selectedIndex = i;
                    });

                    createOverlayEntry(i, context);
                  },
                  child: Container(
                    alignment: Alignment.center,
                    width: 30,
                    child: Text('${elements[i]}'),
                  ),
                ),
              ),
            );
          },
          separatorBuilder: (c, i) {
            return Container(width: 10, height: 10);
          },
        ),
      ),
    );
  }
}
Related