Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

flutter: child widget not rebuilt after parent rebuild

Tags:

flutter

dart

Version:
Flutter-Version: 1.12.14 channel dev
Dart-Version: 2.7.0

Question:
I wan write a Todo App. when i click floatbutton add a new Todo, but in some cases its not work well.

The problem in Scaffold.body, detials in code.

it work well when i use TodoPage(todoList: _todoList).

_pageList.elementAt(_activeIndex) is not work when i submit textfield .

I found the print('Build Home')print after submit but print('Build TodoPage') not print.

why???

My Code:

import 'package:flutter/material.dart';

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

class App extends StatelessWidget{
  @override
  Widget build(BuildContext context){
    return MaterialApp(
      title: 'TodoList',
      home: Home(),
    );
  }
}


class Home extends StatefulWidget{
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home>{
  List<String> _todoList = ['a', 'b', 'c'];
  TextEditingController _controller;
  List<Widget> _pageList;
  int _activeIndex;
  Widget _curPage;

  @override
  void initState(){
    super.initState();
    _activeIndex = 0;
    _pageList = [TodoPage(todoList: _todoList,), OtherPage()];
    _curPage = _pageList[_activeIndex];
    _controller = TextEditingController();
  }

  @override
  Widget build(BuildContext context){
    print('build Home');
    return Scaffold(
      appBar: AppBar(title: Text('Todo'),),
      body: _pageList.elementAt(_activeIndex), // this is not work
      // body: TodoPage(todoList: _todoList,), // this is work well
      floatingActionButton: FloatingActionButton(
        onPressed: _openDlg,
        child: Icon(Icons.add),
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.list), title: Text('Todo')),
          BottomNavigationBarItem(icon: Icon(Icons.favorite), title: Text('Other')),
        ],
        currentIndex: _activeIndex,
        selectedItemColor: Colors.blue,
        onTap: _onMenuTap,
      ),
    );
  }


  _onMenuTap(int index){
    setState(() {
      _activeIndex = index;
    });
  }

  _openDlg(){
    showDialog(
      context: context,
      builder: (BuildContext context){
        return SimpleDialog(
          children: <Widget>[
            TextField(
              controller: _controller,
            ),
            SimpleDialogOption(
              child: FloatingActionButton(child: Text('submit'), onPressed: _addTodo,),
            )
          ],
        );
      }
    );
  }

  _addTodo(){
    print(_controller.text);
    setState(() {
      _todoList.add(_controller.text);
    });
  }
}

class TodoPage extends StatefulWidget{
  TodoPage({Key key, this.todoList}): super(key: key);
  List<String> todoList;
  _TodoPageState createState() => _TodoPageState();
}

class _TodoPageState extends State<TodoPage>{

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

  @override 
  Widget build(BuildContext context){
    print('build TodoPage');
    return Column(
      children: _buildTodoList(),
    );
  }

  List <Widget> _buildTodoList(){
    return widget.todoList.map((todo){
      return Text(todo, style: TextStyle(fontSize: 30),);
    }).toList();
  }
}


class OtherPage extends StatelessWidget{
  @override
  Widget build(BuildContext context){
    return Center(child: Text('Other Page'));
  }
}


like image 498
daxw Avatar asked Oct 22 '25 03:10

daxw


1 Answers

That is logical.

You are reusing an existing instance of a Widget, and widgets are immutable. As such, the framework notice that the instance of the widget did not change and doesn't call build to optimize performances.

Your problem being, you violated the rule of widgets being immutable, which makes this optimization break your app.

What you did:

class MyState extends State<MyStatefulWidget> {
  SomeWidget myWidget = SomeWidget()..someProperty = "initial value";

  void onSomething() {
    setState(() {
      myWidget.someProperty = "new value";
    });
  }

  @override
  Widget build(BuildContext context) {
    return myWidget;
  }
}

What you should instead do:

class MyState extends State<MyStatefulWidget> {
  SomeWidget myWidget = SomeWidget(someProperty: "initial value");

  void onSomething() {
    setState(() {
      myWidget = SomeWidget(someProperty: "new value");
    });
  }

  @override
  Widget build(BuildContext context) {
    return myWidget;
  }
}

Alternatively, just don't cache the widget instance at all.

like image 95
Rémi Rousselet Avatar answered Oct 25 '25 10:10

Rémi Rousselet



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!