Flutter 颤振设计曲线布局为单个小部件

Flutter 颤振设计曲线布局为单个小部件,flutter,flutter-layout,flutter-canvas,Flutter,Flutter Layout,Flutter Canvas,解决了有关设计此布局的问题 我在使用它时遇到了一个问题,因为屏幕右侧的曲线不是小部件,当我想让其他小部件位于绿色区域时,我不能,因为设计的曲线不是小部件,它是从绿色布局裁剪而来的 假设我想把这条曲线放在屏幕的右边,下面是一些小部件 源代码 导入“包装:颤振/材料.省道”; void main(){ runApp(MaterialApp(home:Scaffold(body:Test())); } 类测试扩展了StatefulWidget{ @凌驾 _TestState createState(

解决了有关设计此布局的问题

我在使用它时遇到了一个问题,因为屏幕右侧的曲线不是小部件,当我想让其他小部件位于绿色区域时,我不能,因为设计的曲线不是小部件,它是从绿色布局裁剪而来的

假设我想把这条曲线放在屏幕的右边,下面是一些小部件

源代码

导入“包装:颤振/材料.省道”;
void main(){
runApp(MaterialApp(home:Scaffold(body:Test()));
}
类测试扩展了StatefulWidget{
@凌驾
_TestState createState();
}
类_TestState扩展状态{
双倍高度=0.0;
双宽度=0.0;
双重_-righpadding=2.0;
双倍_btnSize=25.0;
双倍_btnY=0.0;
@凌驾
小部件构建(构建上下文){
如果(_height==0.0)
设置状态(){
_height=MediaQuery.of(context).size.height;
_宽度=MediaQuery.of(context).size.width;
_btnY=_高度/3*2;
});
返回高度==0.0
?容器()
:堆栈(
儿童:[
容器(
颜色:颜色,白色,
),
定制油漆(
大小:大小(_宽度-_右侧填充,_高度),
画家:曲线画家(_btnSize,_btnY),
),
],
);
}
}
类CurvedPainter扩展了CustomPainter{
曲线画师(this.btnSize,this.btnY);
最终的双重认证;
最终双btnY;
@凌驾
空心油漆(帆布,尺寸){
路径=路径();
path.moveTo(0.0,0.0);
path.lineTo(size.width,0.0);
path.lineTo(size.width,btnY-btnSize*2);
path.cubicTo(size.width,btnY-btnSize*0.3,size.width-btnSize*0.95,btnY-btnSize*0.9,size.width-btnSize,btnY);
path.cubicTo(size.width-btnSize*0.95,btnY+btnSize*0.9,size.width,btnY+btnSize*0.3,size.width,btnY+btnSize*2);
path.lineTo(大小.宽度,大小.高度);
path.lineTo(0.0,大小.高度);
path.lineTo(0.0,0.0);
画布绘制路径(
路径
油漆()
…颜色=颜色。透明
..风格=绘画风格。填充);
}
@凌驾
bool应该重新绘制(CurvedPainter oldDelegate)=>oldDelegate.btnY!=btnY;
}

我想使用right curve作为所有小部件之上的小部件,而不使用绿色面,您假设绿色面为
ListView

,似乎需要下图中给出的类似内容

更新:添加了拖动控件

导入'dart:math'作为数学;
进口“包装:颤振/材料.省道”;
void main()=>runApp(MyApp());
类MyApp扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回材料PP(
debugShowCheckedModeBanner:false,
标题:“颤振演示”,
主题:主题数据(
主样本:颜色。蓝色,
),
家:脚手架(
正文:Test(),
),
);
}
}
类测试扩展了StatefulWidget{
@凌驾
_TestState createState();
}
枚举绘图类型{垂直,水平}
类_TestState扩展状态{
最终双精度=48.0;
最终双精度_rightpatting=0;
双重extraOffcet=0;
双_btnY;
双u电流x;
双电流;
双倍高度;
双倍宽度;
bool_isHorizontalActive=真;
bool(u isVerticalActive)=真;
@凌驾
void initState(){
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
super.initState();
}
_后布局{
_height=MediaQuery.of(context).size.height;
_宽度=MediaQuery.of(context).size.width;
_btnY=_高度/2;
setState((){});
}
_昂特拉格(细节){
_更新坐标(
details.globalPosition.dx,
details.globalPosition.dy,
);
}
_更新坐标(双x,双y){
设置状态(){
如果(_isHorizontalActive){
_updateX(x);
}
如果(_是垂直的){
_更新(y);
}
});
}
_updateX(x){
var dx=_currentX-x;
_电流x=x;
_extraOffcet=_extraOffcet+dx;
_extraOffcet=math.max(_extraOffcet,_rightPadding);
_extraOffcet=数学最小值(_extraOffcet,_width-_btnSize);
}
_更新(y){
var dy=_currentY-y;
_电流y=y;
_btnY=_btnY-dy;
_btnY=数学最大值(_btnY,_btnSize);
_btnY=数学最小值(_btnY,_高度-_btnSize);
}
_listItem(字符串文本,双倍高度){
返回容器(
高度:高度,,
子项:文本(文本,样式:TextStyle(fontSize:20.0)),
);
}
@凌驾
小部件构建(构建上下文){
返回_height==null
?容器()
:堆栈(
儿童:[
容器(
填充:仅限边缘设置(右:30),
颜色:颜色,蓝色,
高度:双无限,
宽度:double.infinity,
子项:列(mainAxisAlignment:mainAxisAlignment.center,子项:[
_建筑开关(类型:dragType.vertical),
_建筑开关(类型:dragType.horizontal),
…List.generate(
4.
(索引)=>_列表项('inside',80),
),
]),
),
定位(
右:_extraOffcet+_rightPadding,
孩子:定制油漆(
尺寸:尺寸(尺寸、高度),
画家:曲线画家(_btnSize,_btnY),
),
),
定位(
顶部:_btnY-_btnSize/2+5,
右:_extraOffcet+_rightPadding+5,
儿童:手势检测器(
onPanDown:(详细信息){
_currentX=details.globalPosition.dx;
_currentY=details.global
import 'dart:math' as math;
import 'package:flutter/material.dart';

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

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

class Test extends StatefulWidget {
  @override
  _TestState createState() => _TestState();
}

enum dragType { vertical, horizontal }

class _TestState extends State<Test> {
  final double _btnSize = 48.0;
  final double _rightPadding = 0;

  double _extraOffcet = 0;
  double _btnY;
  double _currentX;
  double _currentY;
  double _height;
  double _width;

  bool _isHorizontalActive = true;
  bool _isVerticalActive = true;

  @override
  void initState() {
    WidgetsBinding.instance.addPostFrameCallback(_afterLayout);

    super.initState();
  }

  _afterLayout(_) {
    _height = MediaQuery.of(context).size.height;
    _width = MediaQuery.of(context).size.width;
    _btnY = _height / 2;
    setState(() {});
  }

  _onDrag(details) {
    _updateCoordinates(
      details.globalPosition.dx,
      details.globalPosition.dy,
    );
  }

  _updateCoordinates(double x, double y) {
    setState(() {
      if (_isHorizontalActive) {
        _updateX(x);
      }
      if (_isVerticalActive) {
        _updateY(y);
      }
    });
  }

  _updateX(x) {
    var dx = _currentX - x;
    _currentX = x;
    _extraOffcet = _extraOffcet + dx;
    _extraOffcet = math.max(_extraOffcet, _rightPadding);
    _extraOffcet = math.min(_extraOffcet, _width - _btnSize);
  }

  _updateY(y) {
    var dy = _currentY - y;
    _currentY = y;
    _btnY = _btnY - dy;
    _btnY = math.max(_btnY, _btnSize);
    _btnY = math.min(_btnY, _height - _btnSize);
  }

  _listItem(String text, double height) {
    return Container(
      height: height,
      child: Text(text, style: TextStyle(fontSize: 20.0)),
    );
  }

  @override
  Widget build(BuildContext context) {
    return _height == null
        ? Container()
        : Stack(
            children: <Widget>[
              Container(
                padding: EdgeInsets.only(right: 30),
                color: Colors.blue,
                height: double.infinity,
                width: double.infinity,
                child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
                  _buildSwitch(type: dragType.vertical),
                  _buildSwitch(type: dragType.horizontal),
                  ...List.generate(
                    4,
                    (index) => _listItem('inside', 80),
                  ),
                ]),
              ),
              Positioned(
                right: _extraOffcet + _rightPadding,
                child: CustomPaint(
                  size: Size(_btnSize, _height),
                  painter: CurvedPainter(_btnSize, _btnY),
                ),
              ),
              Positioned(
                top: _btnY - _btnSize / 2 + 5,
                right: _extraOffcet + _rightPadding + 5,
                child: GestureDetector(
                  onPanDown: (details) {
                    _currentX = details.globalPosition.dx;
                    _currentY = details.globalPosition.dy;
                  },
                  onPanStart: _onDrag,
                  onPanUpdate: _onDrag,
                  child: Material(
                    type: MaterialType.circle,
                    color: Colors.white,
                    elevation: 8.0,
                    child: Container(
                      width: _btnSize - 10,
                      height: _btnSize - 10,
                      child: Icon(Icons.add),
                    ),
                  ),
                ),
              ),
              Positioned(
                top: 0,
                left: _width - _extraOffcet,
                child: Container(
                  color: Colors.white,
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: List.generate(8, (index) => _listItem('from right', 20)),
                  ),
                  height: _height,
                  width: _width,
                ),
              ),
            ],
          );
  }

  SwitchListTile _buildSwitch({dragType type}) {
    Function onChange;
    String titleText, sutitleText;
    bool value;

    if (type == dragType.horizontal) {
      value = _isHorizontalActive;
      titleText = 'Horizontal dragging';
      onChange = (newValue) => setState(() => _isHorizontalActive = newValue);
    } else {
      value = _isVerticalActive;
      titleText = 'Vertical dragging';
      onChange = (newValue) => setState(() => _isVerticalActive = newValue);
    }
    sutitleText = value ? 'active' : 'disabled';

    return SwitchListTile(
      value: value,
      onChanged: onChange,
      title: Text(titleText),
      subtitle: Text(sutitleText),
    );
  }
}

class CurvedPainter extends CustomPainter {
  CurvedPainter(this.btnSize, this.btnY);

  final double btnSize;
  final double btnY;

  @override
  void paint(Canvas canvas, Size size) {
    var halfBtnSize = btnSize / 2;
    var xMax = size.width;
    var yMax = size.height;

    var path = Path()
      ..moveTo(halfBtnSize, yMax)
      ..lineTo(halfBtnSize, btnY + halfBtnSize * 2)
      ..cubicTo(halfBtnSize, btnY + halfBtnSize, 0, btnY + halfBtnSize, 0, btnY)
      ..cubicTo(0, btnY - halfBtnSize, halfBtnSize, btnY - halfBtnSize, halfBtnSize,
          btnY - halfBtnSize * 2)
      ..lineTo(halfBtnSize, 0)
      ..lineTo(xMax, 0)
      ..lineTo(xMax, yMax);

    canvas.drawPath(
        path,
        Paint()
          ..color = Colors.white
          ..style = PaintingStyle.fill);
  }

  @override
  bool shouldRepaint(CurvedPainter oldDelegate) => oldDelegate.btnY != btnY;
}