【问题标题】:How to pass down data from Stateful classes to another Stateful class that exists in another file?如何将数据从有状态类传递到另一个文件中存在的另一个有状态类?
【发布时间】:2019-01-12 16:36:41
【问题描述】:

我在传递已在文本表单字段中填写并在下拉菜单中选择的数据时遇到问题。

我正在尝试使用 Map 函数来传递字符串值,以便将来我也可以传递所有类型的值(例如 int、bool、double 等),但是它不起作用,所以我需要有人来看看。

main.dart

import 'package:flutter/material.dart';
import 'package:workoutapp/auth/auth.dart';
import 'package:workoutapp/auth/root_page.dart';
import 'package:workoutapp/inheritedWigets/auth_provider.dart';

void main(List<String> args) {
  runApp(
    WorkoutManager(),
  );
}

class WorkoutManager extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return AuthProvider(
      auth: Auth(),
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        title: 'Workout Manager',
        home: RootPage(),
        theme: ThemeData(
          primaryColor: Colors.indigo,
          primarySwatch: Colors.indigo,
          accentColor: Colors.indigoAccent,
          hintColor: Colors.indigo,
          brightness: Brightness.dark,
        ),
      ),
    );
  }
}

主页

import 'package:flutter/material.dart';
import 'package:workoutapp/inheritedWigets/auth_provider.dart';

import './profile_account_page.dart';
import './routines_create_page.dart';

import '../objects/Routines/routines_manager.dart';

import '../tools/custom_drawer.dart';

class HomePage extends StatelessWidget {
  final VoidCallback onSignedOut;
  final List<Map<String, String>> routines;

  HomePage({Key key, this.onSignedOut, this.routines}) : super(key: key);

  void _signedOut(BuildContext context) async {
    try {
      var auth = AuthProvider.of(context).auth;
      await auth.signOut();
      onSignedOut();
    } catch (e) {
      print(e);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Workout Manager', style: TextStyle(color: Colors.white)),
        centerTitle: false,
        actions: <Widget>[
          FlatButton(
            child: Text('Logout'),
            onPressed: () {
              return _signedOut(context);
            },
          ),
          IconButton(
            icon: Icon(Icons.account_box),
            tooltip: 'Profile Account',
            color: Colors.white,
            onPressed: () {
              return Navigator.push(context,
                  MaterialPageRoute(builder: (BuildContext context) {
                return ProfileAccountPage();
              }));
            },
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () {
          Navigator.push(context,
              MaterialPageRoute(builder: (BuildContext context) {
            return RoutinesPageCreate();
          }));
        },
      ),
      body: RoutinesManager(),
      drawer: CustomDrawer(),
    );
  }
}

例程管理器

import 'package:flutter/material.dart';
import 'package:workoutapp/objects/routines/routines.dart';

class RoutinesManager extends StatefulWidget {
  final Map<String, String> startingRoutine;

  RoutinesManager({this.startingRoutine});

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

class _RoutinesManagerState extends State<RoutinesManager> {
  List<Map<String, String>> _routines = [];

  @override
  void initState() {
    if (widget.startingRoutine != null) {
      _routines.add(widget.startingRoutine);
    }
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Expanded(
          child: Routines(_routines),
        )
      ],
    );
  }
}

例程创建页面

import 'package:flutter/material.dart';
import 'package:workoutapp/pages/home_page.dart';

class RoutinesPageCreate extends StatefulWidget {
  @override
  _RoutinesPageCreateState createState() => _RoutinesPageCreateState();
}

class _RoutinesPageCreateState extends State<RoutinesPageCreate> {
  final formKey = GlobalKey<FormState>();
  List<Map<String, String>> _routines = [];

  String _routineName, _routineDescription;

  var _routineNameController = TextEditingController();
  var _routineDescriptionController = TextEditingController();

  List<DropdownMenuItem<String>> _dropdownListBodyPartMenuItem = [];
  List<String> _dropdownListBodyPart = [
    'Chest',
    'Back',
    'Leg',
    'Shoulder',
    'Abs',
  ];
  String _selectedBodyPart;

  List<DropdownMenuItem<String>> _dropdownListDayOfWeekMenuItem = [];
  List<String> _dropdownListDayOfWeek = [
    'Monday',
    'Tuesday',
    'Wednesday',
    'Thursday',
    'Friday',
    'Saturday',
    'Sunday',
  ];
  String _selectedDayOfWeek;

  void loadBodyPartData() {
    _dropdownListBodyPartMenuItem = [];
    _dropdownListBodyPartMenuItem = _dropdownListBodyPart.map((val) {
      return DropdownMenuItem<String>(
        child: Text(val),
        value: val,
      );
    }).toList();
  }

  void loadDayOfWeekData() {
    _dropdownListDayOfWeekMenuItem = [];
    _dropdownListDayOfWeekMenuItem = _dropdownListDayOfWeek.map((val) {
      return DropdownMenuItem<String>(
        child: Text(val),
        value: val,
      );
    }).toList();
  }

  final _scaffoldState = GlobalKey<ScaffoldState>();

  void _showSnakBarReset() {
    _scaffoldState.currentState.showSnackBar(
      SnackBar(
        backgroundColor: Theme.of(context).accentColor,
        content: Text('Showing SnackBar TEST'),
      ),
    );
  }

  void _showSnakBarCreateWorkoutRoutine() {
    _scaffoldState.currentState.showSnackBar(
      SnackBar(
        backgroundColor: Theme.of(context).accentColor,
        content: Text('Workout Routine has been created'),
      ),
    );
  }

  void _addRoutine(Map<String, String> routine) {
    setState(() {
      _routines.add(routine);
    });
  }

  @override
  Widget build(BuildContext context) {
    loadBodyPartData();
    loadDayOfWeekData();
    return Scaffold(
      key: _scaffoldState,
      appBar: AppBar(
        title: Text('Create Routines'),
      ),
      body: Container(
        padding: EdgeInsets.all(15.0),
        child: Form(
          key: formKey,
          child: ListView(children: buildInputs() + buildCreateButtons()),
        ),
      ),
    );
  }

  List<Widget> buildInputs() {
    TextStyle textStyle = Theme.of(context).textTheme.title;
    return [
      TextFormField(
          controller: _routineNameController,
          validator: (value) {
            if (value.length > 20) {
              return 'Not a valid Routine Name';
            }
          },
          onSaved: (value) {
            return _routineName = value;
          },
          decoration: InputDecoration(
              labelStyle: textStyle,
              labelText: 'Routine Name',
              hintText: 'Enter the Routine Name for this day',
              border: OutlineInputBorder(
                borderRadius: BorderRadius.circular(5.0),
              ))),
      Padding(padding: EdgeInsets.all(7.0)),
      TextFormField(
          controller: _routineDescriptionController,
          validator: (value) {
            if (value.length > 50) {
              return 'Invalid: The Description must be 50 characters or less.';
            }
          },
          onSaved: (value) {
            return _routineDescription = value;
          },
          decoration: InputDecoration(
              labelStyle: textStyle,
              labelText: 'Description',
              hintText: 'Enter the description of the Routine.',
              border: OutlineInputBorder(
                borderRadius: BorderRadius.circular(5.0),
              ))),
      Padding(padding: const EdgeInsets.all(7.0)),
      Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          DropdownButtonHideUnderline(
              child: DropdownButton(
                  value: _selectedBodyPart,
                  items: _dropdownListBodyPartMenuItem,
                  hint: Text('Select Body Part', style: textStyle),
                  onChanged: (value) {
                    setState(() {
                      _selectedBodyPart = value;
                    });
                  })),
          Padding(
            padding: const EdgeInsets.all(1.0),
          ),
          DropdownButtonHideUnderline(
            child: DropdownButton(
              value: _selectedDayOfWeek,
              items: _dropdownListDayOfWeekMenuItem,
              hint: Text('Select Day of Week', style: textStyle),
              onChanged: (value) {
                setState(() {
                  _selectedDayOfWeek = value;
                });
              },
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(4.0),
          )
        ],
      ),
    ];
  }

  List<Widget> buildCreateButtons() {
    return [
      Padding(
        padding: const EdgeInsets.all(5.0),
        child: Row(
          children: <Widget>[
            Expanded(
              child: RaisedButton(
                  textColor: Theme.of(context).primaryColorDark,
                  color: Theme.of(context).accentColor,
                  child: Text('Create Workout Routine'),
                  onPressed: () {
                    if (formKey.currentState.validate()) {
                      _showSnakBarCreateWorkoutRoutine();
                      formKey.currentState.save();
                      _addRoutine({
                        'routineName': 'Chest Workout',
                        'description': 'Heavy',
                        'bodyPart': 'Chest',
                        'week': 'Monday',
                      });
                      Navigator.push(context,
                          MaterialPageRoute(builder: (BuildContext context) {
                        return HomePage();
                      }));
                    } else {
                      return null;
                    }
                  }),
            ),
            Expanded(
              child: RaisedButton(
                textColor: Theme.of(context).primaryColorLight,
                color: Theme.of(context).primaryColorDark,
                child: Text('Reset'),
                onPressed: () {
                  setState(() {
                    _showSnakBarReset();
                    formKey.currentState.reset();
                    _selectedBodyPart = null;
                    _selectedDayOfWeek = null;
                  });
                },
              ),
            ),
          ],
        ),
      ),
    ];
  }
}

例程

import 'package:flutter/material.dart';
import 'package:workoutapp/objects/routines/routines_detail.dart';

class Routines extends StatelessWidget {
  final List<Map<String, String>> routines;

  Routines(this.routines);

  Widget _buildRoutinesItem(BuildContext context, int index) {
    TextStyle textStyle = Theme.of(context).textTheme.title;
    return Expanded(
      child: Card(
        margin: EdgeInsets.all(5.0),
        child: Column(
          children: <Widget>[
            Padding(
                padding: const EdgeInsets.all(5.0),
                child: Text(routines[index]['routineName'], style: textStyle)),
            Padding(
                padding: const EdgeInsets.all(5.0),
                child: Text(routines[index]['description'], style: textStyle)),
            Padding(
                padding: const EdgeInsets.all(5.0),
                child: Text(routines[index]['bodyPart'], style: textStyle)),
            Padding(
                padding: const EdgeInsets.all(5.0),
                child: Text(routines[index]['week'], style: textStyle)),
            Padding(
              padding: const EdgeInsets.all(5.0),
              child: ButtonBar(
                alignment: MainAxisAlignment.center,
                children: <Widget>[
                  FlatButton(
                    child: Text('Details'),
                    onPressed: () {
                      return Navigator.push(context,
                          MaterialPageRoute(builder: (BuildContext context) {
                        return RoutinesDetail(
                            routines[index]['routineName'],
                            routines[index]['description'],
                            routines[index]['bodyPart'],
                            routines[index]['week']);
                      }));
                    },
                  )
                ],
              ),
            )
          ],
        ),
      ),
    );
  }

  Widget _buildRoutinesList(context) {
    TextStyle textStyle = Theme.of(context).textTheme.title;
    Widget routinesCards = Container(
      child: Container(
        child: Center(
          child: Text("No routines found, please add some.", style: textStyle),
        ),
      ),
    );
    if (routines.length > 0 || routines.length <= 7) {
      ListView.builder(
        itemBuilder: _buildRoutinesItem,
        itemCount: routines.length,
      );
    }
    return routinesCards;
  }

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

例程详细信息页面

import 'package:flutter/material.dart';

class RoutinesDetail extends StatelessWidget {
  final String routineName, description, bodyPart, week;

  RoutinesDetail(this.routineName, this.description, this.bodyPart, this.week);

  @override
  Widget build(BuildContext context) {
    TextStyle textStyle = Theme.of(context).textTheme.title;
    return Scaffold(
      appBar: AppBar(
        title: Text(routineName),
        centerTitle: true,
      ),
      body: Container(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
                padding: const EdgeInsets.all(5.0),
                child: Text(routineName, style: textStyle)),
            Padding(
                padding: const EdgeInsets.all(5.0),
                child: Text(description, style: textStyle)),
            Padding(
                padding: const EdgeInsets.all(5.0),
                child: Text(bodyPart, style: textStyle)),
            Padding(
                padding: const EdgeInsets.all(5.0),
                child: Text(week, style: textStyle)),
            Container(
              padding: EdgeInsets.all(5.0),
              child: RaisedButton(
                child: Text('Delete'),
                onPressed: () {
                  Navigator.pop(context);
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

如您所见,我试图将代码尽可能地分成多个文件,这样它更“可读”,并且让我自己在将来需要时可以轻松地对代码进行更改。 问题是,它被吐出来了,我不明白如何使用数据并将其向下或向上传递到页面或小部件,因为有多个有状态和无状态小部件应该协同工作以使这个应用程序成为可能。

您会在 HomePage 文件 (StatelessWidget) 上注意到,我正在尝试使用位于不同文件中的 RoutinesManager StatefulWidget 显示 Scaffold 主体参数。同时在 HomePage 文件中,我有一个 Scaffold floatingActionButton 参数,它将带您到 RoutinesCreatePage StatefulWidget 以使用 ListView.builder() 创建卡片列表 (StatelessWidget)。但是,在 RoutinesCreatePage 中按下“创建锻炼例程”RaisedButton 后,主页下不会创建卡片,也不会传递任何数据。

有人可以在这里帮助我,因为我完全无能为力。此外,我是 Flutter/dart 方面的初学者,因此具有相对易于理解的解释的解决方案将非常有帮助。

注意:我确实有其他文件有助于这个应用程序,但我认为它们不是问题的一部分,所以我故意将它们排除在外。

如果需要更多信息,请告诉我。

谢谢!

【问题讨论】:

    标签: dart flutter


    【解决方案1】:

    您似乎误解了 Flutter 中的状态。简而言之,状态是属于该特定小部件的内部状态/数据/...。 StatefulWidget 具有确定 UI 是否应在其自身状态更改时重新呈现的状态。外部小部件永远不会知道其他小部件的状态。

    所以这意味着,RoutinesCreatePage 小部件内部发生的任何状态变化,只有 RoutinesCreatePage 知道并做出反应。除非,您通知其他小部件知道发生了变化。

    好吧,说到导航,它就像一个堆栈结构。 HomePage 触发推送到RoutinesCreatePage,然后返回,你需要pop,而不是另一个push

    这里快速修复你的代码,你可以试试。

    主页

    floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () {
            handleNewRoutine();  <---  this is to handle navigation and retrieve returning data from pop
        },  
    ),
    
    Future handleNewRoutine() async {
    
        var newRoutine = await Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => RoutinesPageCreate());
        )
    
        if (newRoutine == null) {
            // nothing returns from RoutinesPageCreate widget
            // so do nothing then
        } else {
            // add to routine list 
            // and trigger list re-rendering
            setState(() {
                this.routines.add(newRoutine); 
            });
        }
    }
    

    RoutinesCreatePage:单击提交按钮时,只需从输入字段中填充所有数据,制作对象模型并弹出以将数据返回到此小部件被推送的位置。

    onPressed: () {
        var newRoutine = .... // populate from UI to create new Routine model object.
        Navigator.pop(context, newRoutine);
    }
    

    另外,花点时间阅读 Flutter 官方文档中的导航指南。这部分非常详细。 https://flutter.io/docs/cookbook/navigation/returning-data

    您的代码中的一些附加 cmets:

    • RoutinesCreatePage 中你不需要知道应用程序级别的状态,我的意思是_routines 变量是不必要的。您只需要一个对象来存储新例程即可弹回HomePage
    • Routines 中,此方法Widget _buildRoutinesList(context) 具有未使用的ListView 创建。

      if (routines.length > 0 || routines.length <= 7) { ListView.builder( itemBuilder: _buildRoutinesItem, itemCount: routines.length, ); }

    【讨论】:

    • 嗨@皮特!非常感谢您为我的问题提供答案,并为缺乏有关 Flutter 导航的知识而道歉。我完全理解你的意思,文档确实澄清了你所说的。我真的很抱歉问,但是我可以问一下,在提交按钮下,在这种情况下你将如何创建新的 Routine 模型对象???我是否创建一个单独的类来存储来自每个文本字段和下拉列表的数据?例子越多越好……我最诚挚的歉意。
    • @JohnT。是的,只是一个具有属性的类。 Routine({this.name, this.bodyPart, this.description, this.week}) 所以你不必像 Map&lt;String, String&gt; 那样存储复杂的东西。
    猜你喜欢
    • 1970-01-01
    • 2016-11-30
    • 2021-07-25
    • 2019-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-12
    • 2021-01-02
    相关资源
    最近更新 更多