flutter setState не добавляет виджет

#flutter #widget #setstate

#flutter #виджет #setstate

Вопрос:

Это мой код. Предполагается, что он добавляет плитку списка, когда вы вводите город в поле выше, а затем нажимаете кнопку «Плюс». Но это не так! Существует ListView, в котором есть все листы списка.Предполагается, что ListView отображает все листы списка в списке. Когда я нажимаю кнопку, печатается сообщение с указанием количества городов. Он показывает, что город добавлен, но на экране ничего не меняется. ColorfulBox — это виджет, который я создал. Это просто украшение.

 import 'package:flutter/material.dart';
import 'package:learner/logic/Weather.dart';
import 'ColorfulBox.dart';

class CitiesScreen extends StatefulWidget{
  @override
  _CitiesScreenState createState() => _CitiesScreenState();
}
class _CitiesScreenState extends State<CitiesScreen>{
  List<Weather> weatherData = List<Weather>();
  String cityName;
  List<Widget> cities = List<Widget>();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 10),
        child: Column(
          children: <Widget>[
            DecoratedBox(
              decoration: BoxDecoration(color: Colors.blueAccent, borderRadius: BorderRadius.circular(10),
              gradient: LinearGradient(colors: [Colors.blue, Colors.purple]) ),
              child: ColorfulBox(Padding(
                padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 0),
                child: ListTile(
                  title: TextField(onChanged: (value){
                    cityName = value;
                  },
                  style: TextStyle(fontSize: 20, color: Colors.white),),
                  trailing: FlatButton(
                    child: Icon(Icons.add, size: 30, color: Colors.white,),
                    onPressed: (){
                      setState(() {
                        addCity(cityName);
                        print(cities.length);
                      });
                    },
                  ),
                ),
              ),
              )
            ),
            SizedBox(height: 30,),
            Expanded(
              child: ListView(
                shrinkWrap: true,
                scrollDirection: Axis.vertical,
                children: cities,
              ),
            )
          ],
        ),
      ),
    );
  }

  void addCity(String city){
    weatherData.add(Weather(city: city));
    Container toBeAddedCity = Container(
      margin: const EdgeInsets.symmetric(vertical: 20, horizontal: 10),
      child:  Text(city, style: TextStyle(fontSize: 22, color: Colors.white),),
    );
    cities.add(ColorfulBox(toBeAddedCity));
  }

}
 

Можете ли вы помочь мне исправить это? Понятия не имею!
Это потому, что метод addCity отсутствует в методе сборки? или он добавляет, но не показывает его?

Ответ №1:

Вы можете просто использовать ListView.builder(...) для создания своих виджетов. Пожалуйста, не сохраняйте виджеты как состояния.

Пример:

 class CitiesScreen extends StatefulWidget {
  @override
  _CitiesScreenState createState() => _CitiesScreenState();
}

class _CitiesScreenState extends State<CitiesScreen> {
  final TextEditingController _controller = TextEditingController();
  final List<Weather> _weatherData = <Weather>[];
  final Random _rand = Random();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 10),
        child: Column(
          children: <Widget>[
            DecoratedBox(
              decoration: const BoxDecoration(
                color: Colors.blueAccent,
                borderRadius: BorderRadius.all(Radius.circular(10)),
                gradient: LinearGradient(
                  colors: <Color>[Colors.blue, Colors.purple],
                ),
              ),
              child: Container(
                //ColorfulBox(
                color: Colors.blue,
                child: Padding(
                  padding: const EdgeInsets.symmetric(vertical: 10),
                  child: ListTile(
                    title: TextField(
                      controller: _controller,
                      style: const TextStyle(fontSize: 20, color: Colors.white),
                    ),
                    trailing: FlatButton(
                      child: const Icon(
                        Icons.add,
                        size: 30,
                        color: Colors.white,
                      ),
                      onPressed: () {
                        setState(() {
                          _weatherData.add(Weather(city: _controller.text));
                        });
                        _controller.clear();
                      },
                    ),
                  ),
                ),
              ),
            ),
            const SizedBox(height: 30),
            Expanded(
              child: ListView.builder(
                shrinkWrap: true,
                itemCount: _weatherData.length,
                itemBuilder: (_, int index) => ListTile(
                  tileColor: Color.fromARGB(
                    255,
                    _rand.nextInt(255),
                    _rand.nextInt(255),
                    _rand.nextInt(255),
                  ),
                  title: Text(
                    _weatherData[index].city,
                    style: const TextStyle(
                      fontSize: 22,
                      color: Colors.white,
                    ),
                  ),
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
}
 

Ответ №2:

Прежде всего, удобный способ получить значение текстового поля — использовать TextEditingController , вместо использования переменной:

 // Create a controller
final TextEditingController _textController = TextEditingController();

// Then use it in your TextField 
TextField(
   controller: _textController,

   // ... other lines

   trailing: FlatButton(
   onPressed: () {

     // Use the TextController's value
     setState(() {
       addCity(_textController.text);
     });

     // Clear the text after using it
     _textController.clear();
     },
   ),
),
 

Поскольку вы обновляете только содержимое cities массива, но сохраняете тот же cities массив, что и дочерний элемент ListView, Flutter предполагает, что состояние не изменяется и, следовательно, не обновляет пользовательский интерфейс. Есть 2 способа решить эту проблему:

  1. Изменение всего списка (не рекомендуется для большого количества элементов):
 // Set new value to the list, instead of just adding items in
cities = List.from(cities)..add(ColorfulBox(toBeAddedCity)); 
 
  1. Используйте ListView.builder() это, чтобы отслеживать длину вашего списка (который будет изменен при добавлении / удалении элементов) (рекомендуется)
 ListView.builder(
   itemCount: cities.length,
   itemBuilder: (context, index) => cities[index]
   // ... other lines
 

Рабочий код:

 
import 'package:flutter/material.dart';
import 'package:learner/logic/Weather.dart';
import 'ColorfulBox.dart';

class CitiesScreen extends StatefulWidget {
  @override
  _CitiesScreenState createState() => _CitiesScreenState();
}

class _CitiesScreenState extends State<CitiesScreen> {
  final TextEditingController _textEditingController = TextEditingController();
  List<Weather> weatherData = List<Weather>();
  String cityName;
  List<Widget> cities = List<Widget>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 10),
        child: Column(
          children: <Widget>[
            DecoratedBox(
                decoration: BoxDecoration(color: Colors.blueAccent,
                    borderRadius: BorderRadius.circular(10),
                    gradient: LinearGradient(
                        colors: [Colors.blue, Colors.purple])),
                child: ColorfulBox(Padding(
                  padding: const EdgeInsets.symmetric(
                      vertical: 10, horizontal: 0),
                  child: ListTile(
                    title: TextField(
                      controller: _textEditingController,
                      style: TextStyle(fontSize: 20, color: Colors.white),),
                    trailing: FlatButton(
                      child: Icon(Icons.add, size: 30, color: Colors.white,),
                      onPressed: () {
                        setState(() {
                          addCity(_textController.text);
                          _textController.clear();
                          print(cities.length);
                        });
                      },
                    ),
                  ),
                ),
                )
            ),
            SizedBox(height: 30,),
            Expanded(
              child: ListView.builder(
                shrinkWrap: true,
                scrollDirection: Axis.vertical,
                itemCount: cities.length,
                itemBuilder: (context, index) => cities[index],
              ),
            )
          ],
        ),
      ),
    );
  }

  void addCity(String city) {
    weatherData.add(Weather(city: city));
    Container toBeAddedCity = Container(
      margin: const EdgeInsets.symmetric(vertical: 20, horizontal: 10),
      child: Text(city, style: TextStyle(fontSize: 22, color: Colors.white),),
    );
    cities.add(ColorfulBox(toBeAddedCity));
  }
}
 

Комментарии:

1. Большое вам спасибо. Он работает отлично. Я новичок в flutter, поэтому я делаю ужасные ошибки! 🙂