首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >从父函数到子函数调用函数(带有参数)

从父函数到子函数调用函数(带有参数)
EN

Stack Overflow用户
提问于 2020-07-22 13:47:27
回答 1查看 5.6K关注 0票数 1

我很难从父母调用一个方法到一个子方法,设置是我的脚手架上有两个孩子,一个listView和一个应用程序条。在应用程序栏中,我有一个searchBar,当我在searchBar上搜索时,我想更新searchBar。

我在溪流上看过颤振小组的视频,但我认为对于这样一个简单的案例来说,这太过分了。我还找到了这个线程"Flutter calling child class function from parent class"和@ehsaneha响应,我认为它对我的用例非常好。我可以使用appBar构造函数将搜索栏中的数据从搜索栏获取到父函数,然后将其从父函数调用到listView!我这样做了,当您不向函数提供任何数据时,它工作得很好,但是只要您需要参数(比如在本例中在搜索栏中输入的字符串),它就不再工作了。我有个错误说:

类型‘动态函数(动态)’的值不能分配给‘动态函数()’类型的变量

在“= updateList”部分:

代码语言:javascript
运行
复制
  _ListViewState(ListViewController _controller) {
    _controller.updateList = updateList;
  }

我在这个问题上有点迷失了,我不确定我是否在正确的方向上,我在我的搜索中没有找到简单的答案。我来自角度,我找不到像一个可以观察到的东西,我可以给我的listView,以便它订阅它容易。

提前感谢您的帮助!

编辑:

在注释之后,我添加了更多代码,以便更详细地查看我的用例:

所以我有一个像这样的homepage.dart

代码语言:javascript
运行
复制
final ListViewController listViewController = ListViewController();

Widget build(BuildContext context) {
 return Scaffold(
   appBar: MyAppBar(setSearchString: setSearchString),
   body: MyListView(controller: listViewController)
 )
}
...

  setSearchString(String searchString) {
    listViewController.updateList();
  }

在我的listView中,我设置了updateList()函数所需的所有内容,如下所示:

代码语言:javascript
运行
复制
class MyListView extends StatefulWidget {
  final ListViewController controller;

  MyListView({this.controller});
  @override
  _MyListViewState createState() => _MyListViewState(controller);
}

class _MyListViewState extends State<MyListView> {
  _MyListViewState(ListViewController _controller) {
    _controller.updateList = updateList;
  }

...
  updateList() {
    print('In updateList !');
  }
}

搜索栏部分只是使用在构造函数中传递的函数将searchText传递给父(homepage.dart),所以我不认为它是必要的

这就是我现在所处的位置,它现在运行良好,但是只要我想向updateList()添加一个参数,比如updateList(String ),它就会给出我前面提到的错误。我希望我添加了加密代码,如果不是,告诉我,我将尝试添加所需的内容!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-07-22 16:37:21

因此,首先,让我向您展示一个如何使用您的方法解决这个问题的例子--,也请阅读代码块中的注释--这些应该有助于更好地理解我在那里做了什么!

:我们不想这样做,因为它会导致对象/函数在任何地方传递,而且维护这样的代码会变得非常困难--对于这个“问题”,有很好的解决方案,在“状态管理”中总结了这一点。

我创建了一个类,用于保存在搜索栏中输入的当前搜索查询:

代码语言:javascript
运行
复制
class SearchModel {
  String searchString = '';
}

然后,我们使用Scaffold小部件和(就像在您的示例中一样) AppBar和ListView的极简视图:

代码语言:javascript
运行
复制
class HomeView extends StatefulWidget {
  @override
  _HomeViewState createState() => _HomeViewState();
}

class _HomeViewState extends State<HomeView> {
  SearchModel _searchModel = SearchModel();

  _updateSearch(String searchQuery) {
    /// The setState function is provided by StatefulWidget, every Widget we
    /// create which extends StatefulWidget has access to this function. By calling
    /// this function we essentially say Flutter we want the Widget (which is coupled
    /// with this state of this StatefulWidget) that we want to rebuild (call the build
    /// function again)
    setState(() {
      _searchModel.searchString = searchQuery;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: PreferredSize(
        /// We pass down the _updateSearch function to our AppBar so when the user
        /// changes the text in the TextField it will update the searchString in our
        /// SearchModel object and call setState so we rebuild HomeViewState (which is
        /// currently the root of our app, so everything gets rebuilded)
          child: MyAppBar(
            searchFunction: _updateSearch,
          ),
          preferredSize: Size.fromHeight(kToolbarHeight)),
          /// In MyListView, where we use the ListView internally to show the results, we
          /// just pass down our SearchModel object where the searchString is maintained
          /// so we can filter our list
      body: MyListView(
        searchModel: _searchModel,
      ),
    );
  }
}

现在我们的AppBar,用户可以输入一些东西并搜索我们的列表:

代码语言:javascript
运行
复制
class MyAppBar extends StatefulWidget {
  /// This is how we declare a function type in Dart where we say which
  /// kind of parameters (here String) it will use
  final Function(String) searchFunction;

  MyAppBar({this.searchFunction});

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

class _MyAppBarState extends State<MyAppBar> {

  @override
  Widget build(BuildContext context) {
    return AppBar(
      title: TextField(
        /// onChanged of TextField needs a function where we pass down
        /// a String and do what we want, thats what the searchFunction is for!
        onChanged: widget.searchFunction,
      ),
    );
  }
}

最后但并非最不重要的是,我们在ListView Widget中显示了一些元素,如果用户更改了AppBar的TextField中的输入,我们希望过滤这些元素,并且只显示那些与searchQuery匹配的元素:

代码语言:javascript
运行
复制
class MyListView extends StatefulWidget {
  final SearchModel searchModel;

  MyListView({this.searchModel});

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

class _MyListViewState extends State<MyListView> {
  List<String> games = [
    'Anno 1602',
    'Final Fantasy 7',
    'Final Fantasy 8',
    'Dark Souls'
  ];

  @override
  Widget build(BuildContext context) {
    return ListView(
      /// Some minimalistic usage of functions which are usable on List objects:
      /// I initialised a random List of game names as Strings and the first thing
      /// I want to do is to filter out all the games which contain the same String
      /// pattern like the searchQuery which the user entered - this is done with the
      /// where function. The where function returns the filtered list, on this filtered
      /// list i use the map function, which takes every object from this list and "maps"
      /// it to whatever we want, here for every game String I want a ListTile Widget which
      /// is being used for our ListView! At the end I have to call toList() since those
      /// functions return so called Iterables instead of List object, "basically" the same
      /// but different classes so we just change the Iterables to List
      children: games
          .where((game) => game
              .toLowerCase()
              .contains(widget.searchModel.searchString.toLowerCase()))
          .map(
            (game) => ListTile(
              title: Text(game),
            ),
          )
          .toList(),
    );
  }
}

好的,这是有效的,,但正如我在一开始所说的那样,这样做将迫使您将对象/函数传递到每个Widget的任何地方!一旦你的应用程序变得足够大,你所有的小部件都会有几十个参数,你会很快忘记你做了什么,维护,改进,扩展你的代码变得几乎不可能。

这就是为什么我们需要一个叫做国家管理的东西!尽管颤振是非常新的,至少与其他著名的框架相比,社区为国家管理提供了许多不同的解决方案/方法。你应该自己读一读,找出对你最好的解决方案--其实很多都取决于个人的喜好。由于我个人喜欢并使用提供者(您可以使用它自己的解决方案)和MobX,所以我将向您展示如何通过使用提供者(https://pub.dev/packages/provider)来处理这个示例:

首先是我们的SearchModel,它现在使扩展了

代码语言:javascript
运行
复制
/// We extend our classes which are basically our "States" with ChangeNotifier
/// so we enable our class to notify all listeners about a change - you will see
/// why!
class SearchModel extends ChangeNotifier {
  String searchString = '';

  /// Now we don't update the searchString variable directly anymore, we use a
  /// function because we need to call notifiyListeners every time we change something
  /// where we want to notify everyone and all the listeners can react to this change!
  updateSearchString(searchQuery) {
    this.searchString = searchQuery;
    notifyListeners();
  }
}

现在我们的新AppBar:

代码语言:javascript
运行
复制
/// Our own Widgets MyAppBar and MyListView are not Stateless! We don't need the state
/// anymore (at least in this example) since we won't use setState anymore and tell
/// our Widgets to rebuild, but will make use of a Widget provided by the Provider
/// package which will rebuild itself dynamically! More later in MyListView
class MyAppBar extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return AppBar(
      title: TextField(
        /// context.watch is also a function added through the Provider package where
        /// we can access objects which have been provided by a Provider Widget (you
        /// will see this in the HomeView implementation) and thats how we now pass
        /// the function instead of passing it down to this Widget manually! Easier right?
        onChanged: context.watch<SearchModel>().updateSearchString,
      ),
    );
  }
}

更新的ListView:

代码语言:javascript
运行
复制
class MyListView extends StatelessWidget {
  final List<String> games = [
    'Anno 1602',
    'Final Fantasy 7',
    'Final Fantasy 8',
    'Dark Souls'
  ];

  @override
  Widget build(BuildContext context) {
    return ListView(
      /// Just like in the MyAppBar implementation we can access our SearchModel object
      /// directly by using context.watch instead of passing it down to our Widget!
      /// again: very nice
      /// The function itself hasn't been changed!
      /// Since we are using the watch function on a object which extends ChangeNotifier,
      /// every time it gets updated, this will get rebuilded automatically! Magic
      children: games
          .where((game) => game.toLowerCase().contains(
              context.watch<SearchModel>().searchString.toLowerCase()))
          .map(
            (game) => ListTile(
              title: Text(game),
            ),
          )
          .toList(),
    );
  }
}

现在我们的HomeView基本上是从这个视图开始的:

代码语言:javascript
运行
复制
/// Every Widget we created manually now is stateless since we don't manage any
/// state by ourself now, which reduces the boilerplate and makes accessing stuff easier!
/// Whats left: in our MyListView and MyAppBar Widgets we accessed the SearchModel
/// object with context.watch ... but how is this possible? Well, we need to provide
/// it somehow of course - thats where the Provider packages gets handy again!
class HomeView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    /// The Provider package has several Provider Widgets, one is the ChangeNotifierProvider
    /// which can be used to provide objects which extend ChangeNotifier, just what we need!
    /// 
    /// Some background: we have to remember that Flutter is basically a big tree. Usually we
    /// use a MaterialApp Widget as the root Widget and after that everything else is being
    /// passed down as a child which will result in a big tree. What we do here: As early as we
    /// need it / want to we place our Provider Widget and "pass down" our Model which should be
    /// accessible for every Widget down the tree. Every Widget which is now under this Provider
    /// (in our case MyAppBar and MyListView) can access this object with context.watch and
    /// work with it
    return ChangeNotifierProvider(
      create: (_) => SearchModel(),
      builder: (context, _) => Scaffold(
        appBar: PreferredSize(
            child: MyAppBar(), preferredSize: Size.fromHeight(kToolbarHeight)),
        body: MyListView(),
      ),
    );
  }
}

读了很多东西--很抱歉让你看了这么长时间,但我想让你知道这个问题是怎么解决的,如何尝试你的方法,以及为什么尽早学习和使用状态管理是如此重要!我希望这给你一个很好的洞察力,为什么它的重要和好,它使颤振更令人敬畏。

在本例中,我使用Provider的方式也只是使用这个状态管理解决方案的许多方法之一--我强烈建议您自己在前面链接的提供者的pub.dev站点上阅读它。有许多例子说明了如何以及何时使用不同的提供者的小部件/方法!

希望这能帮到你!)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63035710

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档