flutterflutter-layoutclipper

Flutter - Trying to achieve this complex layout


I am trying to achieve the following Flutter layout ( as shown in the picture).

enter image description here

I want to place two images side by side which I know can be achieved easily using Row but what I want is to separate the two images by the shape ( as shown in the picture). Also, I am aware of using CustomClipper but I am not able to think of a way to achieve so.

I will be so grateful if somebody can help me. Thanks in advance.


Solution

  • You can use stack for part I with 3 children in this order: - a container for your thunderbolt (or any path you like) color - a first image for Area I - a second image for Area II

    Wrap each image into a ClipPath with a a specific CustomClipper<Path>: the goal is to clip the image to the side on which it should be displayed with a "padding" with the other clipper. In the middle, you will have a zone where no image is clipped, so the first widget in the stack will be displayed.

    Here is the complete source code:

    import 'package:flutter/material.dart';
    
    main() async {
      runApp(
        MaterialApp(
          home: Scaffold(body: ComplexLayoutApp()),
        ),
      );
    }
    
    class ComplexLayoutApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Column(children: [
          Expanded(child: Part1()),
          Expanded(child: Part2()),
        ]);
      }
    }
    
    class Part1 extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Stack(fit: StackFit.expand, children: [
          Container(
            color: Colors.black87,
          ),
          ClipPath(
            clipper: Area1CustomClipper(),
              child: Image.network(
                'https://picsum.photos/seed/area1/400/100',
                fit: BoxFit.fill,
              )),
          ClipPath(
              clipper: Area2CustomClipper(),
              child: Image.network(
                'https://picsum.photos/400/300',
                fit: BoxFit.fill,
              ))
        ]);
      }
    }
    
    class Part2 extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Center(child: Text("Part II")),
          ],
        );
      }
    }
    
    const double offset = 0.34;
    
    class Area1CustomClipper extends CustomClipper<Path> {
    
      @override
      Path getClip(Size size) {
        Path path = Path();
    
        path.moveTo(4*size.width/8, 0);
        path.lineTo((4-offset)*size.width/8, (4)*size.height/8);
        path.lineTo((4)*size.width/8, (4)*size.height/8);
        path.lineTo(size.width/2, size.height);
    
        path.lineTo(0, size.height);
        path.lineTo(0, 0);
        path.close();
        return path;
      }
    
      @override
      bool shouldReclip(CustomClipper<Path> oldClipper) => false;
    }
    
    class Area2CustomClipper extends CustomClipper<Path> {
    
      @override
      Path getClip(Size size) {
        Path path = Path();
    
        path.moveTo(4*size.width/8, 0);
        path.lineTo((4)*size.width/8, (4-offset)*size.height/8);
        path.lineTo((4+offset)*size.width/8, (4 - offset)*size.height/8);
        path.lineTo(size.width/2, size.height);
    
        path.lineTo(size.width, size.height);
        path.lineTo(size.width, 0);
        path.close();
        return path;
      }
    
      @override
      bool shouldReclip(CustomClipper<Path> oldClipper) => false;
    }
    

    And it produces the following UI: enter image description here