Home > Software engineering >  Flutter stack alignment issue
Flutter stack alignment issue

Time:10-11

Good morning, I am trying to align three Widgets by overlapping them with a Stack Widget.

I would like to give the illusion of one single container with two inputs.

I would like that when you select a TextField, the focused border overlapped the divider in the middle.

However, I can't understand why it only works with TextField 1.

Screenshot

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 Scaffold(
      appBar: AppBar(
        title: const Text('Join the Containers'),
      ),
      body: Center(
        child: SizedBox(
          height: 55.0,
          child: Stack(
            children: <Widget>[
              _buildGroundLayer(),
              _buildDividerLayer(),
              _buildInputLayer(),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildGroundLayer() {
    return Container(
      decoration: BoxDecoration(
        color: Colors.black12,
        border: Border.all(
          color: Colors.black87,
          width: 2.5,
        ),
        borderRadius: BorderRadius.circular(16.0),
      ),
    );
  }

  Widget _buildDividerLayer() {
    return Row(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: <Widget>[
        Expanded(
          flex: 1,
          child: Padding(
            padding: const EdgeInsets.symmetric(
              vertical: 8.0,
            ),
            child: Container(
              decoration: const BoxDecoration(
                border: Border(
                  right: BorderSide(
                    color: Colors.black87,
                    width: 2.5,
                  ),
                ),
              ),
            ),
          ),
        ),
        const Expanded(
          flex: 2,
          child: SizedBox(),
        ),
      ],
    );
  }

  Widget _buildInputLayer() {
    return Row(
      children: const <Widget>[
        Flexible(
          flex: 1,
          child: TextField(
            decoration: InputDecoration(
              enabledBorder: OutlineInputBorder(
                borderSide: BorderSide(
                  color: Colors.transparent,
                  width: 2.5,
                ),
                borderRadius: BorderRadius.only(
                  topLeft: Radius.circular(16.0),
                  bottomLeft: Radius.circular(16.0),
                ),
              ),
              focusedBorder: OutlineInputBorder(
                borderSide: BorderSide(
                  color: Colors.orangeAccent,
                  width: 2.5,
                ),
                borderRadius: BorderRadius.only(
                  topLeft: Radius.circular(16.0),
                  bottomLeft: Radius.circular(16.0),
                ),
              ),
              hintText: 'TextField 1',
            ),
          ),
        ),
        Flexible(
          flex: 2,
          child: TextField(
            decoration: InputDecoration(
              enabledBorder: OutlineInputBorder(
                borderSide: BorderSide(
                  color: Colors.transparent,
                  width: 2.5,
                ),
                borderRadius: BorderRadius.only(
                  topRight: Radius.circular(16.0),
                  bottomRight: Radius.circular(16.0),
                ),
              ),
              focusedBorder: OutlineInputBorder(
                borderSide: BorderSide(
                  color: Colors.orangeAccent,
                  width: 2.5,
                ),
                borderRadius: BorderRadius.only(
                  topRight: Radius.circular(16.0),
                  bottomRight: Radius.circular(16.0),
                ),
              ),
              hintText: 'TextField 2',
            ),
          ),
        ),
      ],
    );
  }
}

CodePudding user response:

Actually, all other widgets are stacked but two text fields are in a row filling the screen width. So, their borders won't overlap each other. May I visually represent your code here.

See my visual representation here

So, the optimal way to solve this is to stack two text fields so that text field 1 right border and text field 2 left border are completely overlapped. But, it ain't easy to align them properly. One way to work around is to hide the divider when one of the text fields are tapped so that it creates an illusion. Here is my modified code. Hope that solves your problem. Good day!

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: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget{
  @override
  createState(){
    return _MyHomePageState();
  }
}
class _MyHomePageState extends State<MyHomePage> {
  final FocusNode _focus1 = FocusNode();
  final FocusNode _focus2 = FocusNode();
  bool hasTextFieldSelected = false;
  @override
  void initState() {
    super.initState();
    _focus1.addListener(_onFocusChange);
    _focus2.addListener(_onFocusChange);
  }

  @override
  void dispose() {
    super.dispose();
    _focus1.removeListener(_onFocusChange);
    _focus1.dispose();
    _focus2.removeListener(_onFocusChange);
    _focus2.dispose();
  }
  
  void _onFocusChange() {
    
    setState((){
      hasTextFieldSelected = _focus1.hasFocus || _focus2.hasFocus;
    });
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Join the Containers'),
      ),
      body: Center(
        child: SizedBox(
          height: 55.0,
          child: Stack(
            children: <Widget>[
              _buildGroundLayer(),
              _buildDividerLayer(),
              _buildInputLayer(),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildGroundLayer() {
    return Container(
      decoration: BoxDecoration(
        color: Colors.black12,
        border: Border.all(
          color: Colors.black87,
          width: 2.5,
        ),
        borderRadius: BorderRadius.circular(16.0),
      ),
    );
  }

  Widget _buildDividerLayer() {
    return Row(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: <Widget>[
        const Expanded(
          flex: 1,
          child: SizedBox(),
        ),
        Visibility(
        visible: !hasTextFieldSelected,
          child: Padding(
            padding: const EdgeInsets.symmetric(
              vertical: 8.0,
            ),
            child: Container(
              decoration: const BoxDecoration(
                border: Border(
                  right: BorderSide(
                    color: Colors.black87,
                    width: 2.5,
                  ),
                ),
              ),
            ),
          ),
        ),
        const Expanded(
          flex: 2,
          child: SizedBox(),
        ),
      ],
    );
  }

  Widget _buildInputLayer() {
    return SizedBox(
    height: 55,
    child: Row(
      children: <Widget>[
        Expanded(
          flex: 1,
          child: TextField(
            focusNode: _focus1,
            decoration: const InputDecoration(
              isCollapsed: false,
              enabledBorder: OutlineInputBorder(
                borderSide: BorderSide(
                  color: Colors.transparent,
                  width: 2.5,
                ),
                borderRadius: BorderRadius.only(
                  topLeft: Radius.circular(16.0),
                  bottomLeft: Radius.circular(16.0),
                ),
              ),
              focusedBorder: OutlineInputBorder(
                borderSide: BorderSide(
                  color: Colors.orangeAccent,
                  width: 2.5,
                ),
                borderRadius: BorderRadius.only(
                  topLeft: Radius.circular(16.0),
                  bottomLeft: Radius.circular(16.0),
                ),
              ),
              hintText: 'TextField 1',
            ),
          ),
        ),
        Expanded(
          flex: 2,
          child: TextField(
        focusNode: _focus2,
            decoration: const InputDecoration(
              enabledBorder: OutlineInputBorder(
                borderSide: BorderSide(
                  color: Colors.transparent,
                  width: 2.5,
                ),
                borderRadius: BorderRadius.only(
                  topRight: Radius.circular(16.0),
                  bottomRight: Radius.circular(16.0),
                ),
              ),
              focusedBorder: OutlineInputBorder(
                borderSide: BorderSide(
                  color: Colors.orangeAccent,
                  width: 2.5,
                ),
                borderRadius: BorderRadius.only(
                  topRight: Radius.circular(16.0),
                  bottomRight: Radius.circular(16.0),
                ),
              ),
              hintText: 'TextField 2',
            ),
          ),
        ),
      ],
    ),
    );
  }
}

See result 1 here
See result 2 here

  • Related