使容器颤动,使曲线/凹面如下图所示?

make curve/ concave like below image with container in flutter?

我比较熟悉 ClipPath 小部件,但是借助于此,我只能制作角圆或边框圆和圆。我知道这将由 ClipPathCustomPainter 小部件修复。但不知道该怎么做。

预期图像:

实际结果:

代码:


import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const SampleExample(),
    );
  }
}

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

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        backgroundColor: Colors.red,
        body:  Container(
          alignment: Alignment.bottomCenter,
          height: MediaQuery.of(context).size.height,
          width: MediaQuery.of(context).size.width,
          child: Container(

              height: 250.0,
              decoration:  BoxDecoration(
                color: Colors.white,
                borderRadius: BorderRadius.vertical(
                  top: Radius.elliptical(
                        MediaQuery.of(context).size.width, 120.0)),
                  
              ),
            ),
        ),
        
      );
    }
  }

通过使用 Stack 小部件,我实现了您想要的布局。

  • 绘制白色背景
  • 堆叠底部椭圆红色矩形小部件

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const SampleExample(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: Stack(
        children: [
          Container(
            height: 250.0,
            decoration: BoxDecoration(
              color: Colors.red,
              borderRadius: BorderRadius.vertical(
                  bottom: Radius.elliptical(
                      MediaQuery.of(context).size.width, 120.0)),
            ),
          ),
        ],
      ),
    );
  }
}


decoration>borderRadius 上用 bottom 替换 top 并在正文 Container.

上使用 alignment: Alignment.topCenter 而不是 bottomCenter
body: Container(
  alignment: Alignment.topCenter,
  height: MediaQuery.of(context).size.height,
  width: MediaQuery.of(context).size.width,
  child: Container(
    height: 250.0,
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.vertical(
          bottom: Radius.elliptical(
              MediaQuery.of(context).size.width, 120.0)),
    ),
  ),
),

      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: SafeArea(
            child: ClipPath(
              clipper: CurveClipper(),
              child: Container(
                color: Colors.lightGreen,
                height: 250.0,
                child: Center(
                    child: Padding(
                      padding: EdgeInsets.only(bottom: 50),
                      child: Text(
                        "Curved View",
                        style: TextStyle(
                          fontSize: 25,
                          color: Colors.white,
                        ),
                      ),
                    )),
              ),
            ),
          ),
        );
      }
    }  

 


 class CurveClipper extends CustomClipper<Path> {
      @override
      Path getClip(Size size) {
        int curveHeight = 40;
        Offset controlPoint = Offset(size.width / 2, size.height + curveHeight);
        Offset endPoint = Offset(size.width, size.height - curveHeight);
    
        Path path = Path()
          ..lineTo(0, size.height - curveHeight)
          ..quadraticBezierTo(controlPoint.dx, controlPoint.dy, endPoint.dx, endPoint.dy)
          ..lineTo(size.width, 0)
          ..close();
    
        return path;
      }
    
      @override
      bool shouldReclip(CustomClipper<Path> oldClipper) => false;
    }

一种方法是使用 CustomPainter

class SampleExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      backgroundColor: Colors.red,
      body: Align(
        alignment: Alignment.bottomCenter,
        child: CustomPaint(
          painter: MyCustomPainter(),
          child: SizedBox(
            width: double.infinity,
            height: 250,
            child: Center(child: Text('Cool!')),
          ),
        ),
      ),
    );
  }
}

class MyCustomPainter extends CustomPainter {
  const MyCustomPainter();

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.white
      ..style = PaintingStyle.fill;

    final path = Path()
      ..moveTo(0, 0)
      ..quadraticBezierTo(size.width / 2, size.height / 4, size.width, 0)
      ..lineTo(size.width, size.height)
      ..lineTo(0, size.height)
      ..close();

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}