如何在 Flutter 中让小部件保持在屏幕边框内?

How do I make a widget stay within the screen borders in Flutter?

提问人:İBRAHİM 提问时间:10/26/2023 最后编辑:İBRAHİM 更新时间:10/26/2023 访问量:73

问:

我正在设计一个可调整大小和可重新定位的小部件。我可以将这个小部件保持在上部和左侧屏幕边界内,但我还没有设法将其保持在右侧和下部屏幕边界内。该小部件是使用 GestureDetector 创建的,并通过在 Stack 结构中占据一个位置来定义。我正在尝试在 onPanUpdate 方法中更新小部件位置,但右侧和底部屏幕边界不起作用。如何定义右框和底框边界,并确保小组件保持在这些边界内?top_and_left_frame_part |bottom_and_right_frame_part

这是我的代码块,第一个重要问题:

,onPanUpdate: (details) {
            setState(() {
              if (!isResizing) {
                posx += details.delta.dx / 2;
                posy += details.delta.dy / 2;
                posx = posx.clamp(0, areaW);
                posy = posy.clamp(0, areaH);
                right = posx + width;
                if (right > areaW) {
                  right = areaW;
                }
              }

在此块中,onPanUpdate 方法有效,但 'if' 语句和 'right = areaW' 不起作用。

import 'dart:math';

import 'package:flutter/material.dart';

class Note2Widget extends StatefulWidget {
  const Note2Widget({super.key});

  @override
  State<Note2Widget> createState() => _Note2WidgetState();
}

class _Note2WidgetState extends State<Note2Widget> {
  double width = 100;
  double height = 100;
  double posx = 0;
  double posy = 0;
  late double areaW;
  late double areaH;
  bool isResizing = false;
  Offset startPosition = const Offset(0, 0);
  double right = 0;

  @override
  Widget build(BuildContext context) {
    areaW = MediaQuery.sizeOf(context).width;
    areaH = MediaQuery.sizeOf(context).height;
    return Positioned(
        top: posy,
        left: posx,
        width: areaW,
        height: areaH,
        child: GestureDetector(
          onPanStart: (details) {
            if (details.localPosition.dx >= width - 15 &&
                details.localPosition.dy >= height - 15) {
              setState(() {
                isResizing = true;
                startPosition = details.localPosition;
              });
            }
          },
          onPanUpdate: (details) {
            setState(() {
              if (!isResizing) {
                posx += details.delta.dx / 2;
                posy += details.delta.dy / 2;
                posx = posx.clamp(0, areaW);
                posy = posy.clamp(0, areaH);
                right = posx + width;
                if (right > areaW) {
                  right = areaW;
                }
              }
              if (isResizing) {
                double dx = details.localPosition.dx - startPosition.dx;
                double dy = details.localPosition.dy - startPosition.dy;
                width += dx;
                height += dy;
                startPosition = details.localPosition;
              }
              if (height < 50) {
                height = 50;
              }
              if (width < 50) {
                width = 50;
              }
            });
          },
          onPanEnd: (details) {
            setState(() {
              isResizing = false;
            });
          },
          child: Stack(children: [
            Positioned(
                top: posy,
                left: posx,
                width: width,
                height: height,
                child: Container(
                  decoration: BoxDecoration(
                      border: Border.all(width: 2, color: Colors.black)),
                  child: Center(
                      child: Padding(
                    padding: const EdgeInsets.all(8),
                    child: Container(
                      width: width - 10,
                      height: height - 10,
                      color: Colors.orangeAccent,
                      child: const TextField(
                        textAlign: TextAlign.center,
                        maxLines: null,
                        decoration: InputDecoration(
                          border: InputBorder.none,
                        ),
                        style: TextStyle(
                            color: Colors.black,
                            fontSize: 20,
                            decoration: TextDecoration.none),
                      ),
                    ),
                  )),
                ))
          ]),
        ));
  }
}

这是屏幕上的小部件按钮

class ExampleWidgetButton extends StatefulWidget {
  const ExampleWidgetButton({super.key});

  @override
  State<ExampleWidgetButton> createState() => _ExampleWidgetButtonState();
}

class _ExampleWidgetButtonState extends State<ExampleWidgetButton> {
  @override
  Widget build(BuildContext context) {
    return Positioned(
        bottom: 30,
        left: 60,
        child: CircleAvatar(
          backgroundColor: Colors.amberAccent,
          radius: 35,
          child: IconButton(
              onPressed: () {
                context
                    .read<PageDraggable>()
                    .addWidget(const Note2Widget());
              },
              color: Colors.black,
              icon: const Icon(Icons.pix)),
        ));
  }
}

这是项目的主页

import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
import 'package:provider/provider.dart';
import 'package:star_menu/star_menu.dart';
import 'widgets/interfaceButtons.dart';

double globalPosX = 0;
double globalPosY = 0;
double containerLeft = 0;
double containerTop = 0;
bool isResizing = false;
Offset startPosition = Offset.zero;
String tempText = "";

class PageDraggable extends StatefulWidget with ChangeNotifier {
  PageDraggable({super.key});

  static List<Widget> widgets = [];

  void addWidget(Widget widget) {
    PageDraggable.widgets.add(widget);
    notifyListeners();
    print("${widgets.toList()}");
  }

  void removeWidget(Widget widget) {
    PageDraggable.widgets.remove(widget);
    notifyListeners();
  }

  void allRemoveWidget() {
    PageDraggable.widgets.clear();
    notifyListeners();
  }

  @override
  State<PageDraggable> createState() => _PageDraggableState();
}

class _PageDraggableState extends State<PageDraggable> with ChangeNotifier {
  int count = 0;
  Color pickerColor = const Color(0xff443a49);
  Color currentColor = const Color(0xff443a49);

  void changeColor(Color color) {
    setState(() => pickerColor = color);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Stack(children: <Widget>[
          // AreaControlWidget(),
          Consumer<PageDraggable>(
            builder: (context, value, child) {
              return Positioned(
                  top: 5,
                  left: 250,
                  child: Text("| x: $globalPosX \n| y: $globalPosY "));
            },
          ),
          Consumer<PageDraggable>(
            builder: (context, value, child) {
              return Stack(
                children: [...PageDraggable.widgets],
              );
            },
          ),

          ///Delete Button
          const DeleteWidget(),

          ///
          Positioned(
              bottom: 40,
              left: 120,
              child: ElevatedButton(
                  onPressed: () {
                    showDialog(
                        builder: (context) => AlertDialog(
                              title: const Text('Pick a Color'),
                              content: SingleChildScrollView(
                                child: MaterialPicker(
                                  pickerColor: pickerColor,
                                  onColorChanged: changeColor,
                                ),
                              ),
                              actions: <Widget>[
                                ElevatedButton(
                                  child: const Text('Got it'),
                                  onPressed: () {
                                    setState(() => currentColor = pickerColor);
                                    Navigator.of(context).pop();
                                  },
                                ),
                              ],
                            ),
                        context: context);
                  },
                  child: const Text("Click"))),

          ///
          Positioned(
            bottom: 60,
            right: 30,
            child: StarMenu(
              params: const StarMenuParameters(
                shape: MenuShape.circle,
                circleShapeParams:
                    CircleShapeParams(startAngle: 90, endAngle: 180),
                animationCurve: Curves.bounceInOut,
              ),
              onStateChanged: (state) => print("state changed"),
              onItemTapped: (index, controller) {
                controller.closeMenu!();

                print("Menu item $index tapped");
              },
              items: const [
                ///Create Widget Button
                CreateWidget(),

                ///Create Text Widget Button
                CreateTextWidget(),

                ///Resizeable Widget Button
                ResizeableWidget(),

                ///StickyNote Widget Button
                StickyNoteWidgetButton(),

                ///Example Widget Button
                ExampleWidgetButton(),
              ],
              child: FloatingActionButton(
                onPressed: () {},
                child: const Icon(Icons.looks_one),
              ),
            ),
          )
        ]),
      ),
    );
  }
}

这是main.dart文件

import 'package:draggable_example/ui/pageDraggable.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() => runApp(MultiProvider(
        providers: [
          ChangeNotifierProvider(
            create: (context) => PageDraggable(),
          ),
        ],
        child: MaterialApp(
          home: PageDraggable(),
        )));

所有必要的库都已安装并运行,并且 flutter 正在最新版本上运行。

-flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 3.13.9, on Microsoft Windows [Version 10.0.19045.3570], locale tr-TR)
[√] Windows Version (Installed version of Windows is version 10 or higher)
[√] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[X] Chrome - develop for the web (Cannot find Chrome executable at .\Google\Chrome\Application\chrome.exe)
    ! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable.
[√] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.6.5)
[√] Android Studio (version 2022.3)
[√] VS Code, 64-bit edition (version 1.81.1)
[√] Connected device (3 available)
[√] Network resources

Flutter 提供程序 GestureDetector

评论

0赞 pskink 10/26/2023
请参阅如何防止小组件传递到屏幕边框之外

答: 暂无答案