1

I want to implement this. I could easily achieve it in CSS using position: absolute, a bit of if-else statement in JavaScript, done.

visual explanation

I'm trying to achieve the same thing in Flutter (see code snippet below), I use the Stack widget, it does give the same visual result. But, I cannot click anything inside the options box.

I've done searching for a solution and found that according to the answer of this question, this behaviour is intentional, and I should refactor my code not to use ClipBehavior (Overflow is deprecated). With that being said, I could do something like just using Column instead of Stack but I need the Stack's behaviour where the Options should not push another widgets when it is being shown, similar to position: absolute in CSS.

I am wondering if there is any other widget that do the same thing as Stack but allow me to interact with the Positioned elements/widgets outside of its bound. If there is any, please let me know!

main.dart

import 'package:flutter/material.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 MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      backgroundColor: Colors.blueGrey,
      body: Center(
        child: CustomDropdown(),
      ),
    );
  }
}

class CustomDropdown extends StatefulWidget {
  const CustomDropdown({Key? key}) : super(key: key);

  @override
  State<CustomDropdown> createState() => _CustomDropdownState();
}

class _CustomDropdownState extends State<CustomDropdown> {
  bool showOptions = false;

  @override
  Widget build(BuildContext context) {
    return Stack(
      clipBehavior: Clip.none,
      children: [
        _buildPrimaryButton(),
        if (showOptions) _buildOptions(),
      ],
    );
  }

  Widget _buildPrimaryButton() {
    return Ink(
      decoration: BoxDecoration(
        color: Colors.white,
        border: Border.all(color: Colors.grey.shade200),
        borderRadius: BorderRadius.circular(10),
      ),
      child: InkWell(
        onTap: () {
          setState(() {
            showOptions = !showOptions;
          });
        },
        child: const Padding(
          padding: EdgeInsets.all(10),
          child: Text('Primary Button'),
        ),
      ),
    );
  }

  Widget _buildOptions() {
    return Positioned(
      right: 0,
      bottom: -145,
      child: Ink(
        padding: const EdgeInsets.symmetric(vertical: 10),
        decoration: BoxDecoration(
          color: Colors.white,
          border: Border.all(color: Colors.grey.shade200),
          borderRadius: BorderRadius.circular(10),
        ),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            InkWell(
              onTap: () => print('TAPPED Option 1'),
              child: const Padding(
                padding: EdgeInsets.fromLTRB(20, 10, 150, 10),
                child: Text('Option 1'),
              ),
            ),
            InkWell(
              onTap: () => print('TAPPED Option 2'),
              child: const Padding(
                padding: EdgeInsets.fromLTRB(20, 10, 150, 10),
                child: Text('Option 2'),
              ),
            ),
            InkWell(
              onTap: () => print('TAPPED Option 3'),
              child: const Padding(
                padding: EdgeInsets.fromLTRB(20, 10, 150, 10),
                child: Text('Option 3'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Ken White
  • 123,280
  • 14
  • 225
  • 444
Yura
  • 1,937
  • 2
  • 19
  • 47

0 Answers0