Непонимание флаттера - refre sh и виджет с отслеживанием состояния - PullRequest
0 голосов
/ 12 июля 2020

Я застрял и думаю, что полностью потерялся в логах c Flutter. Я хочу сделать следующее:

class HomeScreen extends StatefulWidget {
  static String id = 'home_screen';

  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
   
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.refresh),
            onPressed: () {
              ** I WANT TO CALL REFRESH ON THE EVENT LIST**
            },
          ),
        ],
      ),
      body: Column(
        children: <Widget>[
          EventList(),
...

class EventList extends StatefulWidget {
  @override
  _EventListState createState() => _EventListState();
}

class _EventListState extends State<EventList> {
  List<Event> eventList = [];

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

  Future<Null> getEventList() async {
   
    // Fill eventList from a web service ...

    setState(() {
  
    });
  }

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Container(
         child: (eventList == null || eventList.length == 0)
            ? Center(
                child: CircularProgressIndicator()
            : ListView.builder(
                itemCount: (eventList == null) ? 0 : eventList.length,
                itemBuilder: (BuildContext context, int index) {
...

Этот logi c работает, чтобы выполнить первую выборку данных с помощью GET в веб-службе.

Как я могу вызвать refre sh (), чтобы получить новые данные на HomeScreen и попросить EventList обновить sh (снова вызвать getEventList ()) и перестроить?

Спасибо

Ответы [ 3 ]

1 голос
/ 14 июля 2020

Есть много способов решить вашу проблему.

  1. Первый - вызвать setState в вашем классе MainState после получения данных. Это приведет к перестройке всех дочерних виджетов. Но для этого вам также необходимо сохранить данные внутри вашего класса MainState и передать их в виджет EventList в качестве аргумента. (Обратите внимание, что ваш виджет EventList больше не должен иметь Stateful! Поскольку он больше не должен вызывать метод setState).

  2. Второй вариант, если вы действительно хотите, чтобы ваша функция была внутри вашего класса EventListState заключается в создании контроллера, который вы создаете в своем классе MainState. Но это довольно большая работа для такой простой задачи, как эта.

  3. Третий - создать Сервис, который будет отдельным классом, предоставляющим поток, на котором вы будете pu sh ваши данные, когда это необходимо. Я знаю, что это может показаться сложным, поэтому вот теоретический пример:

    class MyEventService {
      final StreamController<List<Event>> stateStreamController = StreamController<List<Event>>.broadcast();
    
      Stream<ActionState> get stateStream => stateStreamController.stream;
    
      Future<void> refresh() { fetchFromServer... then stateStreamController.push(data);
    }
    class MainState {
       build(..) => Scaffold(... Button(onPushed: myEventServiceInstance.refresh)
     }
    
    class EventList {
       build(..) => StreamBuilder(stream: myEventServiceInstance.stream...)
    
    }
    
    
    
    

Надеюсь, это помогло вам, не стесняйтесь спрашивать, хотите ли вы более сложный образец!

1 голос
/ 15 июля 2020

Вы можете скопировать и вставить полный код ниже
Рабочая демонстрация имитирует 3-секундную задержку сети и показывает случайное число
Шаг 1: позвоните setState в IconButton s onPressed

IconButton(
  icon: Icon(Icons.refresh),
  onPressed: () {
    setState(() {});
  },
),

Шаг 2: В didUpdateWidget из _EventListState используйте addPostFrameCallback для вызова getEventList()

@override
  void didUpdateWidget(EventList oldWidget) {
    super.didUpdateWidget(oldWidget);
    WidgetsBinding.instance.addPostFrameCallback((_) {
      getEventList();
    });
  }

рабочая демонстрация

введите описание изображения здесь

Последовательность выполнения при нажатии refre sh кнопка

didUpdateWidget
build
getEventList
build for clear EventList
build for add EventList

полный код

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

class Event {
  String title;

  Event({this.title});
}

class HomeScreen extends StatefulWidget {
  static String id = 'home_screen';

  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          actions: <Widget>[
            IconButton(
              icon: Icon(Icons.refresh),
              onPressed: () {
                setState(() {});
              },
            ),
          ],
        ),
        body: Column(children: <Widget>[
          EventList(),
        ]));
  }
}

class EventList extends StatefulWidget {
  @override
  _EventListState createState() => _EventListState();
}

class _EventListState extends State<EventList> {
  List<Event> eventList = [];

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

  @override
  void didUpdateWidget(EventList oldWidget) {
    super.didUpdateWidget(oldWidget);
    WidgetsBinding.instance.addPostFrameCallback((_) {
      getEventList();
    });
  }

  Future<Null> getEventList() async {
    Random random = new Random();

    setState(() {
      eventList.clear();
    });

    await Future.delayed(Duration(seconds: 3), () {});

    List<Event> newEventList = List<Event>.generate(
        3, (index) => Event(title: random.nextInt(100).toString()));

    setState(() {
      eventList.addAll(newEventList);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Expanded(
        child: Container(
            child: (eventList == null || eventList.length == 0)
                ? Center(child: CircularProgressIndicator())
                : ListView.builder(
                    itemCount: (eventList == null) ? 0 : eventList.length,
                    itemBuilder: (BuildContext context, int index) {
                      return ListTile(title: Text(eventList[index].title));
                    })));
  }
}

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: HomeScreen(),
    );
  }
}
0 голосов
/ 13 июля 2020

при флаттере вы не можете вызвать дочернюю функцию от родителя. Я бы рекомендовал объединить их в один виджет с отслеживанием состояния или поместить скаффолд в виджет eventList, чтобы панель приложений и ListView находились в одном виджете statfull

class HomeScreen extends StatefulWidget {
  static String id = 'home_screen';

  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  List<Event> eventList = [];

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

    Future<Null> getEventList() async {
   
    // Fill eventList from a web service ...

    setState(() {
  
    });
  }

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
   
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.refresh),
            onPressed: () => getEventList(),
          ),
        ],
      ),
      body: Column(
        children: <Widget>[
          buildEventList(),
        ]
      ),);

  }




  Widget buildEventList()(BuildContext context) {
    return Expanded(
      child: Container(
         child: (eventList == null || eventList.length == 0)
            ? Center(
                child: CircularProgressIndicator()
            : ListView.builder(
                itemCount: (eventList == null) ? 0 : eventList.length,
                itemBuilder: (BuildContext context, int index) {
..
                }),
    }

  }
...