skip to Main Content

I would like to achive an effect similar to the next one:

enter image description here

The idea is to have a fully displayed Icon in grey, and then in top of it the same Icon but in another color and only showing on the screen a percentage of the widget on top. I’ve tried to do it with Stack and Align (heightFactor) but nothing worked well.

Any ideas?

EDIT: Here it’s the code I tried:

class TestWidget extends StatefulWidget {
  const TestWidget({super.key});

  @override
  State<TestWidget> createState() => _TestWidgetState();
}

class _TestWidgetState extends State<TestWidget> {
  GlobalKey aKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Container(
        color: Colors.grey[200],
        child: Align(
          alignment: const Alignment(-0.85, -0.85),
          child: Wrap(children: [
            Stack(
              children: [
                Center(
                    child: Icon(
                        key: aKey,
                        Symbols.thunderstorm,
                        color: Colors.blueGrey,
                        size: 100)),
                Column(
                  children: [
                    LayoutBuilder(builder: (context, _) {
                      RenderBox renderbox =
                          aKey.currentContext!.findRenderObject() as RenderBox;

                      return SizedBox(height: renderbox.size.height * 0.25);
                    }),
                    const ClipRect(
                        child: Align(
                      heightFactor:
                          0.5, // Set this to 0.8 to see the displaced, unmatching image.
                      child: Icon(Symbols.thunderstorm,
                          color: Colors.red, size: 100),
                    ))
                  ],
                )
              ],
            )
          ]),
        ));
  }
}

These are the outputs for 0.5 and 0.8 heightFactor, respectively:

enter image description here
enter image description here

3

Answers


  1. Chosen as BEST ANSWER

    Inspired by @pskink's answer, I came up with this working example:

    class TestWidget extends StatefulWidget {
      const TestWidget({super.key});
    
      @override
      State<TestWidget> createState() => _TestWidgetState();
    }
    
    class _TestWidgetState extends State<TestWidget> {
      GlobalKey aKey = GlobalKey();
    
      double percent = 0.3;
    
      @override
      Widget build(BuildContext context) {
        return Container(
            color: Colors.grey[200],
            child: Align(
              alignment: const Alignment(-0.85, -0.85),
              child: Wrap(children: [
                Stack(
                  children: [
                    Center(
                        child: Icon(
                            key: aKey,
                            Symbols.thunderstorm,
                            color: Colors.blueGrey,
                            size: 100)),
                    Column(
                      children: [
                        LayoutBuilder(builder: (context, _) {
                          RenderBox renderbox =
                              aKey.currentContext!.findRenderObject() as RenderBox;
    
                          return SizedBox(
                              height: renderbox.size.height * (1 - percent));
                        }),
                        ClipRect(
                          child: Align(
                              heightFactor: percent,
                              alignment: Alignment.bottomCenter,
                              child: const Icon(Symbols.thunderstorm,
                                  color: Colors.red, size: 100)),
                        )
                      ],
                    )
                  ],
                )
              ]),
            ));
      }
    }
    

    Which works as desired. The reason for it is because if you align the coloured widget to the top, the heightFactor cuts starting from there. You only need to insert the new cut widget in a Column with a SizedBox with the height you loose from the coloured one.

    I couldn't figure out how to configure the Rect object @pskink suggested, and it would be appreciated if he could give a detailed code with his full solution to complement this one.


  2. One way to achieve that is using an Stack and two icons, placing one icon above another, and using a ClipRect to cut superior’s icons in half, showing the bottom half of the icon behind. Here’s an example:

    Container(
        width: 100,
        child: Stack(
          children: [
            Icon(
              Icons.bolt,
              size: 100,
              color: Colors.grey,
            ),
            ClipRect(
              child: Align(
                alignment: Alignment.topCenter,
                heightFactor: 0.5,
                child: Icon(
                  Icons.bolt,
                  size: 100,
                  color: Colors.yellow,
                ),
              ),
            ),
          ],
        ))
    
    Login or Signup to reply.
  3. You can use ShaderMask widget to make a gradient color.

    I created this demo so you can see an example here. Run it and click on the icon to see the changes:

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search