flutter, значение индекса, которое я попросил удалить, отличается от значения индекса, которое было фактически удалено в List <Widget> - PullRequest
2 голосов
/ 09 мая 2020
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: Scaffold(body: Center(child: TestWidget())));
  }
}

class MovableItem extends StatefulWidget {
  final int removeIndex;
  final double xInit;
  final double yInit;

  MovableItem({
    @required this.removeIndex,
    @required this.xInit,
    @required this.yInit,
  });

  @override
  State<StatefulWidget> createState() {
    return _MovableItemState();
  }
}

class _MovableItemState extends State<MovableItem> {
  double xPosition = 0;
  double yPosition = 0;

  double _height = 150;
  double _width = 150;

  @override
  void initState() {
    super.initState();
    xPosition = widget.xInit;
    yPosition = widget.yInit;

    print(widget.removeIndex);
  }

  @override
  Widget build(BuildContext context) {

    return Positioned(
      top: yPosition,
      left: xPosition,
      child: GestureDetector(
        onPanUpdate: (DragUpdateDetails tapInfo) {
          setState(() {
            xPosition += tapInfo.delta.dx;
            yPosition += tapInfo.delta.dy;
          });
        },
        child: Row(
          children: <Widget>[
            GestureDetector(
                child: Container(height: _height, width: _width, child: Text(widget.removeIndex.toString()))
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();

    print(widget.removeIndex);
  }
}


class TestWidget extends StatefulWidget {
  TestWidgetState createState() => TestWidgetState();
}

class TestWidgetState extends State<TestWidget> {

  List<Widget> _stack = [];

  @override
  void initState() {
    super.initState();
    _stack.add(MovableItem(removeIndex: 0, xInit: 0, yInit:  0,));
    _stack.add(MovableItem(removeIndex: 1, xInit: 20, yInit: 20,));
  }

  void onChangedFunction(int removeIndex) {
    setState(() {
      _stack.removeAt(removeIndex);
    });
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
        child: Column(
          children: <Widget>[
            RaisedButton(
              child: Text("remove 0 index"),
              onPressed: () {
              setState(() {
                _stack.removeAt(0);
              });

            },),
            SizedBox(
              width: 500,
              height: 500,
              child: Stack(
                children: _stack,
              ),
            ),
          ],
        ));
  }
}

При onPressed в RaisedButton я надеюсь удалить нулевой индекс в _stack. но на самом деле первый индекс удаляется.

при вызове onPressed я получаю следующий результат печати. ​​

1

Я ожидал напечатать ноль. как удалить нулевой индекс в переменной _stack?

в другом коде, первый индекс удаляется в переменной _stack в этом коде.

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: Scaffold(body: Center(child: TestWidget())));
  }
}

class TestWidget extends StatefulWidget {
  TestWidgetState createState() => TestWidgetState();
}

class TestWidgetState extends State<TestWidget> {

  List<Widget> _stack = [];

  @override
  void initState() {
    super.initState();

    _stack.add(Positioned(
      top: 0,
      left: 0,
      child: Text("0"),
    ));

    _stack.add(Positioned(
      top: 20,
      left: 20,
      child: Text("1"),
    ));
  }
  @override
  Widget build(BuildContext context) {
    return SafeArea(
        child: Column(
          children: <Widget>[
            RaisedButton(
              child: Text("remove 0 index"),
              onPressed: () {
              setState(() {
                _stack.removeAt(0);
              });

            },),
            SizedBox(
              width: 500,
              height: 500,
              child: Stack(
                children: _stack,
              ),
            ),
          ],
        ));
  }
}

1 Ответ

0 голосов
/ 10 мая 2020

вам нужно добавить значение ключа к вашим виджетам

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: Scaffold(body: Center(child: TestWidget())));
  }
}

class MovableItem extends StatefulWidget {
  final int removeIndex;
  final double xInit;
  final double yInit;

  MovableItem({
    Key key,
    @required this.removeIndex,
    @required this.xInit,
    @required this.yInit,
  }) : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return _MovableItemState();
  }
}

class _MovableItemState extends State<MovableItem> {
  double xPosition = 0;
  double yPosition = 0;

  double _height = 150;
  double _width = 150;

  @override
  void initState() {
    super.initState();
    xPosition = widget.xInit;
    yPosition = widget.yInit;

    print('created: ${widget.removeIndex} with key ${widget.key}');
  }

  @override
  Widget build(BuildContext context) {

    return Positioned(
      top: yPosition,
      left: xPosition,
      child: GestureDetector(
        onPanUpdate: (DragUpdateDetails tapInfo) {
          setState(() {
            xPosition += tapInfo.delta.dx;
            yPosition += tapInfo.delta.dy;
          });
        },
        child: Row(
          children: <Widget>[
            GestureDetector(
                child: Container(height: _height, width: _width, child: Text(widget.removeIndex.toString()))
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
    print('disposed: ${widget.removeIndex} with key ${widget.key}');
  }
}


class TestWidget extends StatefulWidget {
  TestWidgetState createState() => TestWidgetState();
}

class TestWidgetState extends State<TestWidget> {

  List<Widget> _stack = [];

  @override
  void initState() {
    super.initState();
    _stack.add(MovableItem(key: Key('0'), removeIndex: 0, xInit: 0, yInit:  0,));
    _stack.add(MovableItem(key: Key('1'), removeIndex: 1, xInit: 20, yInit: 20,));
  }

  void onChangedFunction(int removeIndex) {
    setState(() {
      _stack.removeAt(removeIndex);
    });
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
        child: Column(
          children: <Widget>[
            RaisedButton(
              child: Text("remove 0 index"),
              onPressed: () {
              setState(() {
                _stack.removeAt(0);
              });

            },),
            SizedBox(
              width: 500,
              height: 500,
              child: Stack(
                children: _stack,
              ),
            ),
          ],
        ));
  }
}

вывод консоли будет

created: 0 with key [<0>]
created: 1 with key [<1>]
disposed: 0 with key [<0>]

Почему это происходит? Когда вы устанавливаетеState и удаляете виджет с индексом 0, метод сборки перестраивает себя, и _stack больше не тот же объект, он проверяет, что список теперь имеет другую длину, и удаляет последние объекты (те, которые находятся за пределами новой длины). Если это нежелательно, вы должны добавить ключ к своим виджетам, чтобы при перестроении состояние знало, какой из них удалить на основе значения ключа.

Вы также можете проверить этот код, где я меняю значения печати, чтобы вы видели что происходит, когда вы устанавливаетеState и удаляете indexAt (0). didUpdateWidget изменяет и распечатывает различия между старым и новым, чтобы восстановить

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: Scaffold(body: Center(child: TestWidget())));
  }
}

class MovableItem extends StatefulWidget {
  final int removeIndex;
  final double xInit;
  final double yInit;

  MovableItem({
    @required this.removeIndex,
    @required this.xInit,
    @required this.yInit,
  });

  @override
  State<StatefulWidget> createState() {
    return _MovableItemState();
  }
}

class _MovableItemState extends State<MovableItem> {
  double xPosition = 0;
  double yPosition = 0;

  double _height = 150;
  double _width = 150;

  @override
  void initState() {
    super.initState();
    xPosition = widget.xInit;
    yPosition = widget.yInit;

    print('created: ${widget.removeIndex}');
  }

  @override
  void didUpdateWidget(MovableItem oldWidget){
    print('updated: oldWidget ${oldWidget.removeIndex} vs newWidget ${widget.removeIndex}');
    super.didUpdateWidget(oldWidget);
  }

  @override
  Widget build(BuildContext context) {

    return Positioned(
      top: yPosition,
      left: xPosition,
      child: GestureDetector(
        onPanUpdate: (DragUpdateDetails tapInfo) {
          setState(() {
            xPosition += tapInfo.delta.dx;
            yPosition += tapInfo.delta.dy;
          });
        },
        child: Row(
          children: <Widget>[
            GestureDetector(
                child: Container(height: _height, width: _width, child: Text(widget.removeIndex.toString()))
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();

    print('disposed: ${widget.removeIndex}');
  }
}


class TestWidget extends StatefulWidget {
  TestWidgetState createState() => TestWidgetState();
}

class TestWidgetState extends State<TestWidget> {

  List<Widget> _stack = [];

  @override
  void initState() {
    super.initState();
    _stack.add(MovableItem(removeIndex: 0, xInit: 0, yInit:  0,));
    _stack.add(MovableItem(removeIndex: 1, xInit: 20, yInit: 20,));
  }

  void onChangedFunction(int removeIndex) {
    setState(() {
      _stack.removeAt(removeIndex);
    });
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
        child: Column(
          children: <Widget>[
            RaisedButton(
              child: Text("remove 0 index"),
              onPressed: () {
              setState(() {
                _stack.removeAt(0);
              });

            },),
            SizedBox(
              width: 500,
              height: 500,
              child: Stack(
                children: _stack,
              ),
            ),
          ],
        ));
  }
}

Вывод консоли

created: 0
created: 1
updated: oldWidget 0 vs newWidget 1
disposed: 1

Без ключа создаются 2 объекта 0 и 1. Затем я удалил один с индексом 0 и перестроил родительский, дочерние элементы обновились, и теперь (без ключа) виджет с индексом 0 является единственным виджетом, и его значение обновляется с 0 до 1, а виджет с индексом 1 больше не существует ( потому что ваш список теперь имеет длину 1), поэтому он удален.

EDIT: Также, если вы хотите обновить все локальные параметры (xPosition и yPosition) без использования ключей, вы должны сделать это внутри метода didUpdateWidget

@override
  void didUpdateWidget(MovableItem oldWidget){
    print('updated: oldWidget ${oldWidget.removeIndex} vs newWidget ${widget.removeIndex}');
    xPosition = widget.xInit;
    yPosition = widget.yInit;
    super.didUpdateWidget(oldWidget);
  }

таким образом позиции обновятся, когда вы удалите один из виджетов из списка (если вы не хотите использовать ключи)

...