如何在点击时在小部件周围添加突出显示叠加?

发布于 2025-01-13 07:13:44 字数 442 浏览 0 评论 0原文

我正在使用 Flutter 开发简单的设计系统。我想在选择(单击)时突出显示一个小部件,如下图所示,按钮在单击时突出显示。它获得句柄和边框。

具有挑战性的部分:我不希望布局发生变化,因为单击时手柄和边框占用了额外的空间。我希望小部件、手柄和边框重叠,这样它就不会移动其他相邻小部件的位置。

选择前

选择后

在此处输入图像描述

I'm working on simple design system using Flutter. I want to highlight a widget upon selection (click), as you can see in the below images button get highlighted upon click. It gets handle and border.

Challenging part: I don't want layout getting changed as additional space taken by handle and border upon click. I want widget, handle and border are overlaid, so that it wouldn't shift the position of other neighbouring widgets.

before selection

And after selection

enter image description here

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

寻找一个思念的角度 2025-01-20 07:13:44

由于 Clip.noneclipBehavior,您还可以使用 Stack,并从 Stack 中溢出。 。

输入图片此处描述
输入图片此处描述

完整代码

只需将其复制粘贴到 DartPad 中即可查看其实际效果。

import 'package:flutter/material.dart';

const kcPrimary = Color(0xFF001989);
const kcSecondary = Color(0xFF239689);

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.light(),
      debugShowCheckedModeBanner: false,
      home: const HomePage(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: const [
              Text('Blablablablabla'),
              Text('Blablablablabla'),
              Text('Blablablablabla'),
              PlayButton(),
              Text('Blablablablabla'),
              Text('Blablablablabla'),
              Text('Blablablablabla'),
            ],
          ),
        ),
      ),
    );
  }
}

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

  @override
  State<PlayButton> createState() => _PlayButtonState();
}

class _PlayButtonState extends State<PlayButton> {
  bool clicked = false;
  @override
  Widget build(BuildContext context) {
    return Stack(
      clipBehavior: Clip.none,
      children: [
        InkWell(
          onTap: () => setState(() => clicked = !clicked),
          child: _mainButton,
        ),
        if (clicked) ...[
          Positioned.fill(
            child: IgnorePointer(
              child: _overlayBorder,
            ),
          ),
          Positioned(
            top: -20.0,
            left: 0,
            child: _overlayTitle,
          ),
          Positioned(top: 0, right: 0, child: _corner),
          Positioned(bottom: 0, right: 0, child: _corner),
          Positioned(bottom: 0, left: 0, child: _corner),
          Positioned(top: 0, left: 0, child: _corner),
        ],
      ],
    );
  }

  Widget get _mainButton => Container(
        width: 80.0,
        height: 40.0,
        decoration: BoxDecoration(
            border: Border.all(
              color: kcPrimary,
              width: 3.0,
            ),
            borderRadius: const BorderRadius.all(Radius.circular(12))),
        child: Center(
          child: Row(
            mainAxisSize: MainAxisSize.min,
            children: const [
              Icon(Icons.play_arrow),
              Text('Play'),
            ],
          ),
        ),
      );

  Widget get _overlayBorder => Container(
        decoration: BoxDecoration(
          border: Border.all(
            color: kcSecondary,
            width: 3.0,
          ),
        ),
      );

  Widget get _corner => Container(width: 10, height: 10, color: kcSecondary);

  Widget get _overlayTitle => Container(
        height: 20.0,
        width: 48.0,
        color: kcSecondary,
        alignment: Alignment.center,
        child: const Text(
          'Button',
          style: TextStyle(
            color: Colors.white,
            fontSize: 10,
            fontWeight: FontWeight.bold,
          ),
        ),
      );
}

You could also use a Stack with the overlay bleeding out of the Stack thanks to a clipBehavior of Clip.none.

enter image description here
enter image description here

Full code

Just copy paste it in a DartPad to see it in action.

import 'package:flutter/material.dart';

const kcPrimary = Color(0xFF001989);
const kcSecondary = Color(0xFF239689);

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.light(),
      debugShowCheckedModeBanner: false,
      home: const HomePage(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: const [
              Text('Blablablablabla'),
              Text('Blablablablabla'),
              Text('Blablablablabla'),
              PlayButton(),
              Text('Blablablablabla'),
              Text('Blablablablabla'),
              Text('Blablablablabla'),
            ],
          ),
        ),
      ),
    );
  }
}

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

  @override
  State<PlayButton> createState() => _PlayButtonState();
}

class _PlayButtonState extends State<PlayButton> {
  bool clicked = false;
  @override
  Widget build(BuildContext context) {
    return Stack(
      clipBehavior: Clip.none,
      children: [
        InkWell(
          onTap: () => setState(() => clicked = !clicked),
          child: _mainButton,
        ),
        if (clicked) ...[
          Positioned.fill(
            child: IgnorePointer(
              child: _overlayBorder,
            ),
          ),
          Positioned(
            top: -20.0,
            left: 0,
            child: _overlayTitle,
          ),
          Positioned(top: 0, right: 0, child: _corner),
          Positioned(bottom: 0, right: 0, child: _corner),
          Positioned(bottom: 0, left: 0, child: _corner),
          Positioned(top: 0, left: 0, child: _corner),
        ],
      ],
    );
  }

  Widget get _mainButton => Container(
        width: 80.0,
        height: 40.0,
        decoration: BoxDecoration(
            border: Border.all(
              color: kcPrimary,
              width: 3.0,
            ),
            borderRadius: const BorderRadius.all(Radius.circular(12))),
        child: Center(
          child: Row(
            mainAxisSize: MainAxisSize.min,
            children: const [
              Icon(Icons.play_arrow),
              Text('Play'),
            ],
          ),
        ),
      );

  Widget get _overlayBorder => Container(
        decoration: BoxDecoration(
          border: Border.all(
            color: kcSecondary,
            width: 3.0,
          ),
        ),
      );

  Widget get _corner => Container(width: 10, height: 10, color: kcSecondary);

  Widget get _overlayTitle => Container(
        height: 20.0,
        width: 48.0,
        color: kcSecondary,
        alignment: Alignment.center,
        child: const Text(
          'Button',
          style: TextStyle(
            color: Colors.white,
            fontSize: 10,
            fontWeight: FontWeight.bold,
          ),
        ),
      );
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文