Как я могу передать динамические c данные от виджета к виджету? - PullRequest
0 голосов
/ 16 июня 2020

Позвольте мне предварить это, сказав, что я новичок в флаттере / дротике, а также не очень опытный программист.

Я пытаюсь познакомиться с фреймворком и инструментами флаттера, и я пытаюсь просто расширить базовое приложение счетчика c, которое флаттер создает при генерации проекта. Моя цель состоит в том, чтобы приложение отслеживало, когда счетчик «сброшен», сохранял время и подсчет, когда счетчик был в состоянии, а затем отображал эти данные в таблице на другом экране.

Вот что я до сих пор: я создал класс для отслеживания данных:

class CounterRecord {
  int _counter; //Holds the value the counter was at on reset
  DateTime _resetTime; //Holds the time when the counter was reset

  CounterRecord(int _count){
    _counter = _count;
    _resetTime = DateTime.now();
  }

  int getCount() => _counter; //fetch method for count
  DateTime getTime() => _resetTime; //Fetch method for resettime
}

Вот главный класс / домашняя страница:

import 'package:counter_app/clickerScreen.dart';
import 'package:counter_app/dataScreen.dart';
import 'package:flutter/material.dart';

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

final clickerKey = new GlobalKey<ClickerScreenState>();

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.deepOrange,
        accentColor: Colors.grey,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  //Enables the passing in of the title, clicker screen instance, and datacreen isntance, respectively,
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

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


class _MyHomePageState extends State<MyHomePage> {
  //We don't want a brand new clickerScreen every time, so I'm keeping it up here.
  ClickerScreen clickerScreen = ClickerScreen(clickerKey: clickerKey); //Creates a new clickerScreen - the key points to it too.
  @override
  Widget build(BuildContext context) {
    //Creates an instance (State?) of clickerScreen for the first tab
    return DefaultTabController( //A wrapper that helps manage the tab states
      length: 2, //Currently there are only two options for screens
      child: Scaffold(
        appBar: AppBar( //This represnts the bar up at the top
          title: Text(widget.title),
          bottom: TabBar(
          tabs: [
            //These are the icons for the two tabs we're using
            //The order of these is important: It goes in the same order as TabBarView below
            Tab(icon: Icon(Icons.home)),
            Tab(icon: Icon(Icons.directions_run)),
          ],
          )
        ),
        body: TabBarView(
          children: [
            clickerScreen,
            DataScreen( //this DataScreen will be built every time based on the new data from clickerScreen
              data: clickerKey.currentState.getRecords(),
             ),
          ],
        ),
      ),
    );
  }
}


class CounterRecord {
  int _counter; //Holds the value the counter was at on reset
  DateTime _resetTime; //Holds the time when the counter was reset

  CounterRecord(int _count){
    _counter = _count;
    _resetTime = DateTime.now();
  }

  int getCount() => _counter; //fetch method for count
  DateTime getTime() => _resetTime; //Fetch method for resettime
}

Вот важная часть моего clickerScreen file:

class ClickerScreen extends StatefulWidget {
  ClickerScreen({Key clickerKey}) : super(key: clickerKey);
  @override
  ClickerScreenState createState(){
    return ClickerScreenState();
  }
}

class ClickerScreenState extends State<ClickerScreen> {

  int _counter = 0;
  List<CounterRecord> records;

/* All three of these functions do very similar things, modify the counter value. */
void _resetCounter(){
    setState(() {
      records.add(CounterRecord(_counter));
      _counter = 0;
    });
  }

List<CounterRecord> getRecords(){
  return records;
}

В clickerScreen есть метод сборки, который просто отображает кнопки и текст. Я не назначаю там ключ, так как он просто возвращает виджет Center, но я читал некоторые вещи, которые предполагают, что, возможно, мне стоит это сделать.

И вот мой файл dataScreen:

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

class DataScreen extends StatefulWidget{
  //Enables the passing in of the instance of the clicker screen instance
  DataScreen({Key key, @required this.data}) : super(key: key);
  final List<CounterRecord> data;

  @override
  _DataScreenState createState(){
    return _DataScreenState();
  }
}
class _DataScreenState extends State<DataScreen>{
  @override
  Widget build(BuildContext context) {
    return Text(widget.data.toString());
  }
}

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

Когда я запускаю это, я получаю ошибку NoSuchMethod в getRecords (), получатель: null. Я также попытался вызвать createState () в виджете ClickerScreen в качестве последней попытки.

Есть какой-нибудь совет?

(Я вставил сюда весь файл clickerScreen (https://pastebin.com/j6Y8M8F3), поскольку я не хотел делать этот пост дольше, чем он есть.)

Ответы [ 2 ]

0 голосов
/ 16 июня 2020

Я последовал совету Лео Летто и вместо этого использовал InheritedWidget, расположенный на самом верху, который содержал список записей.

0 голосов
/ 16 июня 2020

Если у вас есть два виджета в зависимости от одного и того же состояния, вы должны использовать что-то, называемое «поднятие состояния вверх». Это означает, что состояние является частью ближайшего виджета, у которого оба других виджета являются дочерними. В вашем случае это будет виджет MyHomePage, содержащий список CounterRecord. Он передает список через конструктор в DataScreen и передает обратный вызов onReset в ClickerScreen.

MyHomePage:

class MyHomePage extends StatefulWidget {
  //Enables the passing in of the title, clicker screen instance, and datacreen isntance, respectively,
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  List<CounterRecord> counterRecord = []; //this is the lifted up state

  onReset(int count) {
    setState(() {
      counterRecord.add(CounterRecord(count));
    });
  }

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      //A wrapper that helps manage the tab states
      length: 2, //Currently there are only two options for screens
      child: Scaffold(
        appBar: AppBar(
            //This represnts the bar up at the top
            title: Text(widget.title),
            bottom: TabBar(
              tabs: [
                //These are the icons for the two tabs we're using
                //The order of these is important: It goes in the same order as TabBarView below
                Tab(icon: Icon(Icons.home)),
                Tab(icon: Icon(Icons.directions_run)),
              ],
            )),
        body: TabBarView(
          children: [
            ClickerScreen(onReset: onReset),
            DataScreen(
              data: counterRecord, //pass the record data to the datascreen
            ),
          ],
        ),
      ),
    );
  }
}

ClickerScreen:

class ClickerScreen extends StatefulWidget {
  final Function(int) onReset;
  ClickerScreen({Key clickerKey, this.onReset}) : super(key: clickerKey);
  @override
  ClickerScreenState createState() {
    return ClickerScreenState();
  }
}

class ClickerScreenState extends State<ClickerScreen> {
  int _counter = 0;

/* All three of these functions do very similar things, modify the counter value. */
  void _resetCounter() {
    widget.onReset(_counter); //call the onReset callback with the counter
    setState(() {
      _counter = 0;
    });
  }

  @override
  Widget build(BuildContext context) {
    ....
  }
}

DataScreen (может быть без сохранения состояния, так как состояние находится в родительском)

class DataScreen extends StatelessWidget{
  //Enables the passing in of the instance of the clicker screen instance
  DataScreen({Key key, @required this.data}) : super(key: key);
  final List<CounterRecord> data;

  @override
  Widget build(BuildContext context) {
    return Text(widget.data.toString());
  }


}

Использование этого простого подхода может быстро утомить и потребовать множества изменений при перемещении виджета в дереве виджетов. Вот почему существует расширенное управление состоянием, такое как Provider с ChangeNotifier или Blo c.

Вот хорошее прочтение по этому поводу:

https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple

...