Flutter布局约束插件cassowary的使用

发布于 1周前 作者 gougou168 来自 Flutter

Flutter布局约束插件cassowary的使用

Cassowary in Dart

Pub Build Status Coverage Status Gitpod Ready-to-Code

Cassowary是Dart中实现的Cassowary约束求解算法。最初的实现基于用C++编写的Kiwi toolkit。它实现了Cassowary论文中描述的功能子集,并为Flutter的需求做出了特定的调整。

The Solver

求解器(Solver)是接受约束并尝试更新成员变量以满足这些约束的对象。

Parameters

为了创建约束,用户需要从视图层次结构中的元素获取特定的参数对象,并从中创建表达式。然后可以从这些表达式中获得约束。如果求解器需要更新这些参数以满足约束,它将调用这些参数上的回调函数。

Constructing Constraints

约束是以ax + by + cz + ... + k == 0形式的线性方程。约束不必是等式关系,也可以指定小于或大于等于(<=>=)的关系。此外,每个约束都指定了一个优先级以帮助解决约束歧义。系统也可能被过度约束。

约束作为一个整体由Constraint对象的实例表示。这反过来又引用了一个Expressionax + by + cz + ... k)、关系和最终的优先级。

每个表达式由一个Term列表和一个常数组成。项ax具有系数a和变量x。提供给用户的Param只是这个变量的包装器,负责检测其变化并更新层次结构中的底层视图。

以下示例设置了一个约束,指定元素的宽度必须至少为100个单位。假设已经从相关视图中获取了leftright这两个Param对象。

var widthAtLeast100 = right - left >= cm(100.0);

让我们一步一步地解释:表达式right - left创建了一个Expression对象的实例。该表达式由两个项组成。rightleft参数包装变量。它们的系数分别为1.0和-1.0,常数为-100.0。常数需要用CM装饰以辅助Dart的操作符重载机制。

所有变量都是不受限制的。因此,没有什么可以阻止求解器使左右边缘变为负值。我们可以通过指定另一个约束来表达我们对这种情况的偏好:

var edgesPositive = (left >= cm(0.0));

当我们构建这些约束时,默认情况下它们是在Priority.required下创建的。这意味着求解器将抵制添加在两个必需约束之间存在歧义的约束。要指定较低的优先级,可以使用priority设置器或在构造约束时使用|符号与优先级一起使用。例如:

var edgesPositive = (left >= cm(0.0))..priority = Priority.weak;

一旦构建了一组约束,它们就被添加到求解器中,并刷新解决方案的结果。

solver
    ..addConstraints([widthAtLeast100, edgesPositive])
    ..flushUpdates();

Edit Constraints

当需要更新作为求解器一部分的参数时,可以使用编辑变量。通过以下示例说明这一点:在鼠标按下时,我们希望更新视图的中点,并让leftright参数自动更新(受已设置的约束影响)。

我们创建一个参数来表示鼠标坐标。

var mid = Variable(coordinate);

然后,我们添加一个约束,用我们已经拥有的参数表达中点。

solver.addConstraint((left + right).equals(Term(mid, 1.0) * cm(2.0)));

然后,我们指定我们打算编辑中点。随着更新的发生,我们告诉求解器满足所有其他约束(尽管我们的示例非常简单)。

solver.addEditVariable(mid, Priority.strong);

最后

solver.flushUpdates();

示例代码

以下是一个完整的示例代码,展示了如何使用Cassowary插件进行布局约束:

import 'package:cassowary/cassowary.dart';

void main() {
  final solver = Solver();
  final left = Param(10);
  final right = Param(20);
  final widthAtLeast100 = right - left >= cm(100);
  final edgesPositive = (left >= cm(0))..priority = Priority.weak;
  solver
    ..addConstraints([widthAtLeast100, edgesPositive])
    ..flushUpdates();

  print('left: ${left.value}, right: ${right.value}');

  final mid = Variable(15);
  // It appears that == isn't defined
  solver
    ..addConstraint((left + right).equals(Term(mid, 1) * cm(2)))
    ..addEditVariable(mid, Priority.strong)
    ..flushUpdates();

  print('left: ${left.value}, mid: ${mid.value}, right: ${right.value}');
}

通过以上内容,您可以了解如何在Flutter项目中使用Cassowary插件来进行布局约束管理。根据实际需求,您可以进一步扩展和调整约束条件,以实现更复杂和灵活的布局效果。


更多关于Flutter布局约束插件cassowary的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter布局约束插件cassowary的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,cassowary 是一个用于处理布局约束的库,它允许开发者以类似于 Auto Layout(在 iOS 开发中使用)的方式来定义复杂的布局。虽然 Flutter 原生已经提供了强大的布局系统(如 ColumnRowStack 等),但 cassowary 可以为需要更高级约束控制的场景提供额外的灵活性。

以下是一个使用 cassowary 插件的基本示例。请注意,由于 cassowary 在 Flutter 社区中可能不是一个广为人知的库(相对于 Flutter 自带的布局系统),并且库的 API 和可用性可能会随时间变化,因此以下代码可能需要根据实际的 cassowary 版本进行调整。

首先,确保你已经在 pubspec.yaml 文件中添加了 cassowary 依赖:

dependencies:
  flutter:
    sdk: flutter
  cassowary: ^x.y.z  # 替换为最新版本号

然后,运行 flutter pub get 来安装依赖。

下面是一个使用 cassowary 插件的示例代码:

import 'package:flutter/material.dart';
import 'package:cassowary/cassowary.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Cassowary Layout Example'),
        ),
        body: CustomLayoutWidget(),
      ),
    );
  }
}

class CustomLayoutWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // 创建约束求解器
    final solver = Solver();

    // 创建变量(代表每个子控件的宽度、高度和位置)
    final topViewHeight = Variable();
    final topViewWidth = Variable();
    final topViewX = Variable();
    final topViewY = Variable();

    final bottomViewHeight = Variable();
    final bottomViewWidth = Variable();
    final bottomViewX = Variable();
    final bottomViewY = Variable();

    // 添加约束
    solver.addConstraint(topViewHeight == 100);
    solver.addConstraint(topViewWidth == 300);
    solver.addConstraint(topViewX == 0);
    solver.addConstraint(topViewY == 0);

    solver.addConstraint(bottomViewHeight == 100);
    solver.addConstraint(bottomViewWidth == 300);
    solver.addConstraint(bottomViewX == 0);
    solver.addConstraint(bottomViewY == topViewY + topViewHeight + 20); // 底部视图位于顶部视图下方20单位

    // 更新变量值
    solver.updateVariables();

    // 获取最终的布局值
    final topViewSize = Size(topViewWidth.value, topViewHeight.value);
    final topViewPosition = Offset(topViewX.value, topViewY.value);

    final bottomViewSize = Size(bottomViewWidth.value, bottomViewHeight.value);
    final bottomViewPosition = Offset(bottomViewX.value, bottomViewY.value);

    return Stack(
      children: [
        Positioned(
          left: topViewPosition.dx,
          top: topViewPosition.dy,
          width: topViewSize.width,
          height: topViewSize.height,
          child: Container(
            color: Colors.red,
            child: Center(child: Text('Top View')),
          ),
        ),
        Positioned(
          left: bottomViewPosition.dx,
          top: bottomViewPosition.dy,
          width: bottomViewSize.width,
          height: bottomViewSize.height,
          child: Container(
            color: Colors.blue,
            child: Center(child: Text('Bottom View')),
          ),
        ),
      ],
    );
  }
}

在这个示例中,我们创建了两个视图(顶部视图和底部视图),并使用 cassowary 来定义它们的布局约束。这些约束包括视图的尺寸和位置。然后,我们通过 solver.updateVariables() 更新变量的值,并在 Flutter 的布局系统中使用这些值来定位视图。

请注意,这个示例假设 cassowary 插件提供了 SolverVariable 等类和方法。如果 cassowary 的 API 有所变化,或者如果你发现这个库不再维护或推荐使用,请查阅最新的文档或寻找替代方案。

回到顶部