플러터 - 요소를 제거할 때 UI가 올바르게 업데이트되지 않음
사용자가 목록에서 요소를 추가하고 제거할 수 있는 페이지를 구축하려고 합니다. 나는 문제와 씨름하고 있다. 요소를 제거하면 UI가 업데이트되는 동안 모델이 올바르게 업데이트되지만 잘못된 방식입니다. 목록의 마지막 요소만 제거됩니다.
Flower 1.5.4를 쓰고 있어요.
나는 이미 목록에 더 간단한 요소를 사용하고 있으며 가능한 모든 문제를 제거하기 위해 이 페이지만으로 새 프로젝트를 구축하려고 했지만 여전히 제대로 작동하지 않습니다.
나도 List 대신 Column을 사용해봤지만 결과는 항상 같아.
main.dll:
import 'package:flutter/material.dart';
import './widget.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text('Hello'),
),
body: Center(
child: SectionWidget(),
),
),
);
}
}
위젯.widget:
import 'package:flutter/material.dart';
class SectionWidget extends StatefulWidget {
_SectionWidgetState createState() => new _SectionWidgetState();
}
class _SectionWidgetState extends State<SectionWidget> {
List<String> _items = List<String>();
@override
Widget build(BuildContext context) {
List<Widget> children = [
ListTile(
title: Text(
"Strings",
style: TextStyle(fontWeight: FontWeight.bold),
),
trailing: IconButton(
icon: Icon(Icons.add_circle),
onPressed: () => setState(() {
_items.add('item ${_items.length}');
print("Adding "+ _items.last);
}),
),
),
];
children.addAll(_buildForms());
return ListView(
children: children,
);
}
List<Widget> _buildForms() {
List<Widget> forms = new List<Widget>();
print("Strings:" + _items.toString());
for (String item in _items) {
forms.add(
ListTile(
leading: Icon(Icons.person),
title: TextFormField(
initialValue: item,
),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () => setState(() {
print("Removing $item");
_items.remove(item);
}),
),
),
);
}
return forms;
}
}
목록에 항목 4개를 추가한 다음 "항목 1"을 제거하면 콘솔의 출력이 다음과 같습니다:
I/flutter ( 4192): Strings:[]
I/flutter ( 4192): Adding item 0
I/flutter ( 4192): Strings:[item 0]
I/flutter ( 4192): Adding item 1
I/flutter ( 4192): Strings:[item 0, item 1]
I/flutter ( 4192): Adding item 2
I/flutter ( 4192): Strings:[item 0, item 1, item 2]
I/flutter ( 4192): Adding item 3
I/flutter ( 4192): Strings:[item 0, item 1, item 2, item 3]
I/flutter ( 4192): Removing item 1
I/flutter ( 4192): Strings:[item 0, item 2, item 3]
맞습니다. "항목 1"이 목록에서 제거되었지만 UI를 보면 다음과 같습니다:
리스트에 있는 요소의 수는 올바르고 모형은 올바르지만 표시된 요소는 잘못되었습니다.
이거 어떻게 고치죠? 내가 잘못하고 있는 건가요, 아니면 Flower의 버그인가요?
모든 텍스트 필드에 대한 컨트롤러를 만들거나 hintText를 사용해야 한다고 생각합니다
Scaffold(
appBar: AppBar(
title: Text('Hello'),
),
body: ListView(
children: <Widget>[
ListTile(
title: Text(
'Strings',
style: TextStyle(fontWeight: FontWeight.bold),
),
trailing: IconButton(
icon: Icon(Icons.add_circle),
onPressed: () => setState(() {
_items.add('item ${_items.length}');
print('Adding '+ _items.last);
}),
),
),
ListView.builder(
physics: BouncingScrollPhysics(),
shrinkWrap: true,
itemCount: _items.length,
itemBuilder: (BuildContext context, int i){
return ListTile(
leading: Icon(Icons.person),
title: TextFormField(
decoration: InputDecoration(
hintText: _items[i]
),
),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () => setState(() {
print('Removing ${_items[i]}');
_items.remove(_items[i]);
}),
),
);
},
)
]
)
);
위젯 상태에는 문자열 목록만 유지됩니다. 이러한 위젯 중 하나를 제거하고 위젯을 재구성하면 Flutter는 의 하위 위젯이 하나 더 적게 포함되어 있음을 알고 마지막 위젯을 제거하고 다른 위젯을 재구성합니다.
여기서 중요한 점은 텍스트를 설정하는 데 사용한다는 것입니다. 필드의 텍스트를 변경해도 변경되지 않는다는 것입니다. a는 초기 값을 1개만 가질 수 있으며 처음 설정하는 것입니다.
텍스트를 변경하려면 를 사용해야 합니다. 위의 예에서는 를 로 바꾸기만 하면 해결됩니다. 그러나 실제 응용 프로그램에서는 사용자가 입력한 값이 재구성된 후에도 유지되도록 컨트롤러를 위젯 상태로 저장할 수 있습니다.
기능만 업데이트 위젯 데이터를 업데이트하고 위젯을 다시 만들지 않는 것으로 알고 있습니다. 코드에서 initialValue를 사용하여 에 대한 기본 텍스트를 설정합니다. 이 매개 변수는 생성될 때만 사용됩니다. 따라서 항목을 제거하면 의 길이에 따라 위젯이 업데이트됩니다(이 경우 처음 3개는 보관하고 마지막 1개는 제거합니다). 생성된 것이 아니라 재사용되기 때문에 현재 의미가 없습니다.
이 문제를 해결하기 위해 다음 대신 텍스트를 설정할 수 있습니다
먼저 지도 목록을 작성합니다
List<TextEditingController> controllers = [];
그런 다음 항목을 추가할 때 해당 항목에 대한 새 컨트롤러도 생성합니다
onPressed: () => setState(() {
_items.add('item ${_items.length}');
controllers.add(TextEditingController(text: "${_items.length}"));
print("Adding "+ _items.last);
}),
마지막으로 컨트롤러 추가
TextFormField(
controller: controllers[_items.indexOf(item)],
)
아, 품목을 제거하기 전에 컨트롤러를 제거하는 것을 잊지 마십시오
controllers.removeAt(_items.indexOf(item));
도움이 되길 바래요.
삭제된 요소를 업데이트하지 않고 제거하는 이유는 두 번째 위젯이 상태 저장 위젯이기 때문입니다. 경우에 따라 강제 렌더링이 필요합니다
이 코드를 사용하여 didUpdateWidget 메서드를 재정의하여 보기를 다시 로드합니다
@override
void didUpdateWidget(LoadImageFromPathAsync oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget != widget) {
setState(() {});
}
}
TextFormField 요소에 키 또는 컨트롤러 필드를 사용합니다.
TextFormField(
key: GlobalKey(),
....
)
목록에 있는 요소의 길이를 포함하는 에 전달할 수 있습니다.
ListView(
key: Key(myList.length.toString()),
children: myList.map(...),
),
목록의 길이가 변경될 때만 전체가 처음부터 다시 작성됩니다. 중요한 것은 텍스트 필드 내에서 입력하는 것과 같은 다른 경우에는 이러한 작업이 발생하지 않는다는 것입니다. 이러한 작업을 수행하면 모든 문자 유형에 초점을 맞출 수 없기 때문입니다.
도움이 되길 바래 ;)
이것은 해결책이 아니라 설명입니다. 해결책은 Marcin Szawek의 의견을 참조하십시오.
가 예상대로 렌더링되지 않는 것은 상태 저장 위젯이라는 것입니다. 위젯을 재구성할 때 후드 아래에 위젯 트리, 요소 트리 및 렌더링 개체 트리 3개가 있습니다. RenderObject는 시각적 표현이며 요소를 직접 기반으로 합니다. 요소는 직접 위젯을 기반으로 하며 위젯은 구성, 즉 코드에 입력하는 내용에 불과합니다.
위젯을 처음 인스턴스화할 때 해당 요소가 생성됩니다. 그러나 _items 목록을 변경하기 위해 전화를 걸면 위젯이 다시 작성되지만 요소가 삭제되고 다시 작성되지는 않습니다. 대신, 플러터는 초기 위젯과 재구성 위젯 사이의 차이점을 먼저 비교하도록 만들어진다. 이 경우 처음에는 4개의 타일이 있고, 나중에는 3개의 타일이 있습니다. 새 타일을 다시 만드는 대신, 두 번째와 세 번째 타일의 요소는 재구성 위젯의 특성을 갖도록 업데이트됩니다(그리고 위젯/구성이 이전과 이후에 동일하기 때문에 첫 번째 타일에 대한 변경은 발생하지 않습니다). 네 번째 요소는 요소 트리에서 팝됩니다. 공식 Fluter 팀의 이것은 세 그루의 나무를 설명하는 데 훌륭한 역할을 합니다.
Dart의 Dev 도구를 사용하면 세 번째 요소의 키/ID가 변경되지 않습니다. 세 번째를 검사할 때 키 전후는 입니다.
초기의
삭제후
트리에서 요소를 제거한 후 다시 삽입하려면 Marcin Szawek가 설명한 대로 키를 에 부착합니다. 상태 저장 위젯의 경우 해당하는 새 위젯이 동일한 위젯 유형이고 키가 동일한 경우 요소가 업데이트됩니다. 키를 명시적으로 지정하지 않으면 요소는 해당 위젯과 동일한 유형인지 여부만 확인하고 모든 위젯이 런타임 유형이므로 요소가 업데이트되지 않으므로 항목 0과 항목 2 대신 항목 0과 항목 1이 표시됩니다. 키에 대해 자세히 읽어보는 것이 좋습니다.
다트의 데브툴을 사용하면 세 번째 키에서 변화를 찾을 수 있다. 처음에, 그것이 항목 1이었을 때, 키는 이다. 그런 다음 항목 1이 삭제되면 키가 로 변경됩니다.
초기의
삭제후
Marcin Szawek Solution을 사용해 보았지만 BLoC를 사용하여 위젯을 재구축하고 있어 그의 솔루션이 다음과 같은 문제를 일으켰습니다:
1 - 목록에 첫 번째 요소(TextField)를 추가하고 "a"를 입력합니다
2 - 두 번째 요소(TextField)를 목록에 추가합니다(첫 번째 텍스트 필드가 비어 있음)
우리가 BLoC를 사용하지 않을 때 이런 일이 일어날지는 사실 모르겠어요. 하지만 당신이 BLoC를 사용하고 있다면 이 솔루션은 나에게 매력적으로 작용했다:
1- 목록 생성
List<UniqueKey> listKeys = [];
2 - 에 요소를 추가하기 위해 추가 아이콘을 클릭할 때 의 개체를 에 추가해야 합니다
listKeys.add(UniqueKey());
3 - 각 목록 구성원을 항목에 첨부합니다
ListView.builder(
shrinkWrap: true,
itemCount: items?.length ?? 0,
itemBuilder: (context, index) {
return ListItem(
key: listKeys[index];
);
},
)
4 - "TextField" 요소를 제거할 때 다음에서 삭제해야 합니다
listKeys.removeAt(deleteIndex);
'개발하자' 카테고리의 다른 글
수평으로 스크롤할 수 있으며 스냅 효과가 있는 카드(스냅 효과 포함) (0) | 2023.02.19 |
---|---|
유형 스크립트: 익스프레스 연장.자체 클래스가 있는 세션 인터페이스 (0) | 2023.02.18 |
Kubernetes 입력에서 라우팅 경로 제거 (1) | 2023.02.17 |
플러터 오류: 범위 오류(인덱스): 잘못된 값: 0.2 범위에 포함되지 않음: 3 (0) | 2023.02.17 |
Flurter/React-Native/Android-Native 앱용 SHA-1 생성 (0) | 2023.02.16 |