Flutter implementiert Steuerelemente, die im Verhältnis zu Position und Größe angeordnet sind


Vorwort

Bei Videoüberwachungsprojekten ist es erforderlich, mehrere geteilte Bildschirme anzuzeigen, z. B. 2x2, 3x3, 414 usw. Es ist problematisch, wenn jeder geteilte Bildschirm separat implementiert wird, und eine Benutzeranpassung kann nicht unterstützt werden. Der beste Weg besteht darin, einen Allzweck-Split-Screen-Container zu implementieren, und das Abtastverhältnis berechnet die Positionsgröße, die an jede Größe angepasst werden kann.


1. Wie erreicht man es?

Der intuitivste Weg, dies zu erreichen, besteht darin, die Breite und Höhe des Steuerelements zu ermitteln und sie dann proportional zu berechnen. Flatter kann jedoch beim Erstellen keine Positionsbreiten- und -höheninformationen abrufen und kann diese erst nach dem Zeichnen abrufen, sodass dieser Weg nicht einfach ist zu implementieren, und der einfachere Weg sollte die Verwendung von Zeile, Spalte in Kombination mit flexibel sein.

1. Wandeln Sie den Wert in eine Punktzahl um

der umzurechnende Wert

 final Rect rect; //子控件位置大小,比例值范围0-1

Definieren Sie ein Score-Objekt

//分数
class Rational {
    
    
  int den = 1; //分母
  int num = 0; //分子
  Rational(this.num, this.den);
  //通过double构造,accuracy小数点后精度
  factory Rational.fromDouble(double d, {
    
    int accuracy = 5}) {
    
    
    int den = 1;
    while (d > d.toInt() && accuracy-- > 0) {
    
    
      d *= 10;
      den *= 10;
    }
    return Rational(d.toInt(), den);
  }
}

In Brüche umwandeln und Nenner ausrichten

    //将位置大小转成分数
    final width = Rational.fromDouble(rect.width);
    final x = Rational.fromDouble(rect.left);
    final height = Rational.fromDouble(rect.height);
    final y = Rational.fromDouble(rect.top);
    //对齐分母
    if (width.den != x.den) {
    
    
      final den = width.den;
      width.den *= x.den;
      width.num *= x.den;
      x.den *= den;
      x.num *= den;
    }
    //对齐分母
    if (height.den != y.den) {
    
    
      final den = height.den;
      height.den *= y.den;
      height.num *= y.den;
      y.den *= den;
      y.num *= den;
    }

2. Reihe+Flexibles Layout horizontal

Wir verwenden das automatische Layout von Row und die Eigenschaften des proportionalen Layouts von Flexible, um den Flex-Wert zu berechnen, der der Position und Größe des Kontrollverhältnisses gemäß den obigen Bewertungen entspricht.

 Row(
      children: [
        Flexible(
          flex: x.num,
          child: Container(),
        ),
        Flexible(
          flex: width.num,
          child: child/*子控件,加上纵向布局则是Column*/
        ),
        Flexible(flex: width.den - width.num - x.num, child: Container()),
      ],
    );
  }

3. Spalte + flexibles vertikales Layout

Wir verwenden das automatische Layout von Column und die Eigenschaften des proportionalen Layouts von Flexible, um den Flex-Wert zu berechnen, der der Position und Größe des Kontrollverhältnisses gemäß der obigen Bewertung entspricht.

Column(
            children: [
              Flexible(
                flex: y.num,
                child: Container(),
              ),
              Flexible(flex: height.num, child: child/*子控件*/),
              Flexible(
                flex: height.den - height.num - y.num,
                child: Container(),
              ),
            ],
          )

2. Vollständiger Code

Proportion.Dart

import 'package:flutter/material.dart';

//比例布局控件,
class Proportion extends StatelessWidget {
    
    
  final Rect rect; //位置大小,比例值范围0-1
  final Widget child;
  const Proportion({
    
    
    super.key,
    this.rect = const Rect.fromLTWH(0, 0, 1, 1),
    required this.child,
  });

  
  Widget build(BuildContext context) {
    
    
    //实现按比例显示布局
    final width = Rational.fromDouble(rect.width);
    final x = Rational.fromDouble(rect.left);
    final height = Rational.fromDouble(rect.height);
    final y = Rational.fromDouble(rect.top);
    if (width.den != x.den) {
    
    
      final den = width.den;
      width.den *= x.den;
      width.num *= x.den;
      x.den *= den;
      x.num *= den;
    }
    if (height.den != y.den) {
    
    
      final den = height.den;
      height.den *= y.den;
      height.num *= y.den;
      y.den *= den;
      y.num *= den;
    }
    return Row(
      children: [
        Flexible(
          flex: x.num,
          child: Container(),
        ),
        Flexible(
          flex: width.num,
          child: Column(
            children: [
              Flexible(
                flex: y.num,
                child: Container(),
              ),
              Flexible(flex: height.num, child: child),
              Flexible(
                flex: height.den - height.num - y.num,
                child: Container(),
              ),
            ],
          ),
        ),
        Flexible(flex: width.den - width.num - x.num, child: Container()),
      ],
    );
  }
}

//分数
class Rational {
    
    
  int den = 1; //分母
  int num = 0; //分子
  Rational(this.num, this.den);
  //通过double构造,accuracy小数点后精度
  factory Rational.fromDouble(double d, {
    
    int accuracy = 5}) {
    
    
    int den = 1;
    while (d > d.toInt() && accuracy-- > 0) {
    
    
      d *= 10;
      den *= 10;
    }
    return Rational(d.toInt(), den);
  }
}

Gemeinsames Layout (optional)
proportions.dart

import 'package:flutter/material.dart';

import 'proportion.dart';

//常用布局,需配合stack作为父容器使用
class Proportions {
    
    
  Proportions._();
  //全屏
  static List<Proportion> fullScreen({
    
    
    required Widget child,
  }) =>
      [
        Proportion(
          rect: const Rect.fromLTWH(0, 0, 1, 1),
          child: child,
        )
      ];

  //二分屏
  static List<Proportion> halfScreen({
    
    
    required Widget left,
    required Widget right,
  }) =>
      [
        Proportion(
          rect: const Rect.fromLTWH(0, 0, 0.5, 1),
          child: left,
        ),
        Proportion(
          rect: const Rect.fromLTWH(0.5, 0, 0.5, 1),
          child: right,
        ),
      ];

  //四分屏
  static List<Proportion> quadScreen({
    
    
    required List<Widget> children,
  }) {
    
    
    return [
      Proportion(
        rect: const Rect.fromLTWH(0, 0, 0.5, 0.5),
        child: children[0],
      ), //左上
      Proportion(
        rect: const Rect.fromLTWH(0.5, 0, 0.5, 0.5),
        child: children[1],
      ), //右上
      Proportion(
        rect: const Rect.fromLTWH(0, 0.5, 0.5, 0.5),
        child: children[2],
      ), //左下
      Proportion(
        rect: const Rect.fromLTWH(0.5, 0.5, 0.5, 0.5),
        child: children[3],
      ), //右下
    ];
  }

  //6  分屏
  static List<Proportion> sixScreen({
    
    
    required List<Widget> children,
  }) {
    
    
    return [
      Proportion(
        rect: const Rect.fromLTWH(0, 0, 0.666, 0.666),
        child: children[0],
      ), //左上
      Proportion(
        rect: const Rect.fromLTWH(0.666, 0, 0.333, 0.333),
        child: children[1],
      ), //右上
      Proportion(
        rect: const Rect.fromLTWH(0.666, 0.333, 0.333, 0.333),
        child: children[2],
      ), //右中
      Proportion(
        rect: const Rect.fromLTWH(0.666, 0.666, 0.333, 0.333),
        child: children[3],
      ), //右下
      Proportion(
        rect: const Rect.fromLTWH(0.333, 0.666, 0.333, 0.333),
        child: children[4],
      ), //中下
      Proportion(
        rect: const Rect.fromLTWH(0, 0.666, 0.333, 0.333),
        child: children[5],
      ), //左下
    ];
  }

  //8  分屏
  static List<Proportion> eightScreen({
    
    
    required List<Widget> children,
  }) {
    
    
    return [
      Proportion(
        rect: const Rect.fromLTWH(0, 0, 0.75, 0.75),
        child: children[0],
      ), //左上
      Proportion(
        rect: const Rect.fromLTWH(0.75, 0, 0.25, 0.25),
        child: children[1],
      ), //右上
      Proportion(
        rect: const Rect.fromLTWH(0.75, 0.25, 0.25, 0.25),
        child: children[2],
      ), //右中1
      Proportion(
        rect: const Rect.fromLTWH(0.75, 0.5, 0.25, 0.25),
        child: children[3],
      ), //右中2
      Proportion(
        rect: const Rect.fromLTWH(0.75, 0.75, 0.25, 0.25),
        child: children[4],
      ), //右下
      Proportion(
        rect: const Rect.fromLTWH(0.5, 0.75, 0.25, 0.25),
        child: children[5],
      ), //中下2
      Proportion(
        rect: const Rect.fromLTWH(0.25, 0.75, 0.25, 0.25),
        child: children[6],
      ), //中下1
      Proportion(
        rect: const Rect.fromLTWH(0, 0.75, 0.25, 0.25),
        child: children[7],
      ), //左下
    ];
  }

  //9  分屏
  static List<Proportion> nightScreen({
    
    
    required List<Widget> children,
  }) {
    
    
    int n = 0;
    return [
      ...children.getRange(0, 9).map(
        (element) {
    
    
          final i = n++;
          return Proportion(
            rect: Rect.fromLTWH(
              (i % 3) * 0.333,
              (i ~/ 3) * 0.333,
              0.333,
              0.333,
            ),
            child: element,
          );
        },
      )
    ];
  }

  //16  分屏
  static List<Proportion> sixteenScreen({
    
    
    required List<Widget> children,
  }) {
    
    
    int n = 0;
    return [
      ...children.getRange(0, 16).map(
        (element) {
    
    
          final i = n++;
          return Proportion(
            rect: Rect.fromLTWH((i % 4) * 0.25, (i ~/ 4) * 0.25, 0.25, 0.25),
            child: element,
          );
        },
      )
    ];
  }

  //414分屏
  static List<Proportion> fourOneFourScreen({
    
    
    required List<Widget> children,
  }) {
    
    
    int n = 0;
    return [
      //左4
      ...children.getRange(0, 4).map(
        (element) {
    
    
          final i = n++;
          return Proportion(
            rect: Rect.fromLTWH((i ~/ 4) * 0.25, (i % 4) * 0.25, 0.25, 0.25),
            child: element,
          );
        },
      ),
      //中间
      Proportion(
        rect: const Rect.fromLTWH(0.25, 0, 0.5, 1),
        child: children[4],
      ),
      //右边4
      ...children.getRange(5, 9).map(
        (element) {
    
    
          final i = n++ + 8;
          return Proportion(
            rect: Rect.fromLTWH((i ~/ 4) * 0.25, (i % 4) * 0.25, 0.25, 0.25),
            child: element,
          );
        },
      )
    ];
  }
}

3. Anwendungsbeispiel

1. Grundlegende Verwendung

Legen Sie die Position und Größe des untergeordneten Steuerelements fest. Wird im Allgemeinen mit Stack als übergeordnetem Container verwendet

    Proportion(
      rect: Rect.fromLTRB(0, 0, 0.5, 0.5), //子控件位置大小,(0, 0, 0.5, 0.5)表示左上1/4的区域
      child: ColoredBox(color: Colors.red), //子控件
    );

2. Viergeteilter Bildschirm

final List<int> _nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
 Stack(
            children: Proportions.quadScreen(children: [
          ..._nums.map((e) => Container(
                constraints: const BoxConstraints.expand(),
                decoration: BoxDecoration(
                    border: Border.all(color: Colors.deepPurple.shade300)),
                child: Center(child: Text("video $e")),
              ))

Fügen Sie hier eine Bildbeschreibung ein

3. Sechs geteilter Bildschirm

final List<int> _nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
 Stack(
            children: Proportions.sixScreen(children: [
          ..._nums.map((e) => Container(
                constraints: const BoxConstraints.expand(),
                decoration: BoxDecoration(
                    border: Border.all(color: Colors.deepPurple.shade300)),
                child: Center(child: Text("video $e")),
              ))

Fügen Sie hier eine Bildbeschreibung ein

4. Oktaler Bildschirm

final List<int> _nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
 Stack(
            children: Proportions.eightScreen(children: [
          ..._nums.map((e) => Container(
                constraints: const BoxConstraints.expand(),
                decoration: BoxDecoration(
                    border: Border.all(color: Colors.deepPurple.shade300)),
                child: Center(child: Text("video $e")),
              ))

Fügen Sie hier eine Bildbeschreibung ein

5. Neun geteilter Bildschirm

final List<int> _nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
 Stack(
            children: Proportions.nightScreen(children: [
          ..._nums.map((e) => Container(
                constraints: const BoxConstraints.expand(),
                decoration: BoxDecoration(
                    border: Border.all(color: Colors.deepPurple.shade300)),
                child: Center(child: Text("video $e")),
              ))

Fügen Sie hier eine Bildbeschreibung ein

6. 414 geteilte Bildschirme

final List<int> _nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
 Stack(
            children: Proportions.fourOneFourScreen(children: [
          ..._nums.map((e) => Container(
                constraints: const BoxConstraints.expand(),
                decoration: BoxDecoration(
                    border: Border.all(color: Colors.deepPurple.shade300)),
                child: Center(child: Text("video $e")),
              ))

Fügen Sie hier eine Bildbeschreibung ein
Halten Sie immer das Verhältnis ein
Fügen Sie hier eine Bildbeschreibung ein


Zusammenfassen

Das Obige ist das, worüber ich heute sprechen möchte. Dieser Artikel verwendet eine relativ einfache Methode zur Implementierung der proportionalen Layoutsteuerung. Sein Hauptmerkmal ist, dass es flexibel einsetzbar ist, insbesondere um die Realisierung der Video-Split-Screen-Vorschau zu erleichtern. Im Wesentlichen handelt es sich auch um eine Allzwecksteuerung, die aus einer Zusammenfassung einer Klasse von Layoutregeln abgeleitet wird, denn wenn man bedenkt, dass 2x2 und 3x3 immer noch hart codiert werden können, sind es bei 4x4 und 5x5 jedoch 16 oder 25 Parameter sind erforderlich, daher müssen Sie sie stattdessen verwenden. Ein Array bedeutet, dass die Position gemäß den Regeln berechnet werden muss, die mit denen in diesem Artikel identisch sind. Daher sind die Steuerelemente in diesem Artikel von praktischer Bedeutung.

Supongo que te gusta

Origin blog.csdn.net/u013113678/article/details/132035825
Recomendado
Clasificación