如何设置状态并重新渲染一个 ModalRoute

huangapple go评论75阅读模式
英文:

How to set state and re render a ModalRoute

问题

我有一个搜索模态弹窗,用户可以在其中搜索并根据他们的搜索获取结果。然而,由于该类扩展了ModalRoute而不是StatefulWidget,调用setState不会重新渲染构建,搜索结果不会显示在屏幕上。有没有一种方法可以更新状态或重新渲染屏幕,而不必将该类转换为StatefulWidget?

class JobSearchModal extends ModalRoute {
  List<dynamic>? searchResults;

  @override
  Duration get transitionDuration => const Duration(milliseconds: 250);

  @override
  bool get opaque => false;

  @override
  bool get barrierDismissible => false;

  @override
  Color get barrierColor => Colors.black;

  @override
  String? get barrierLabel => null;

  @override
  bool get maintainState => true;

  getSearch(String query) async {
    var result = await SearchService().getAllPaged(query: query);
    setState(() {
      searchResults = result;
    });
  }

  _onSearchChanged(String query) {
    getSearch(query);
  }

  @override
  Widget buildPage(
    BuildContext context,
    Animation<double> animation,
    Animation<double> secondaryAnimation,
  ) {
    return Scaffold(
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Expanded(
                    child: TextField(
                      onChanged: _onSearchChanged,
                      autofocus: true,
                    ),
                  ),
                  TextButton(
                      onPressed: () => Navigator.of(context).pop(),
                      child: const Text('Cancel'))
                ],
              ),
              const SizedBox(
                height: 20,
              ),
              ...searchResults?.map(
                (search) {
                  return ListTile(
                    title: Text(search.title),
                  );
                },
              ) ?? []
            ],
          ),
        ),
      ),
    );
  }
}

请注意,上述代码中的 searchResults 使用了可选链操作符 (?.) 来处理可能为null的情况。

英文:

I have Search Modal popup where the user can search and get results based on their search. However, since the class extends ModalRoute and not a StatefulWidget, calling setState does not re-render the build and the search results do no appear on the screen. Is there a way to update the state or re-render the screen without having to convert the class to a StatefulWidfet?

class JobSerachModal extends ModalRoute {
  List&lt;dynamic&gt;? searchResults;

  @override
  Duration get transitionDuration =&gt; const Duration(milliseconds: 250);

  @override
  bool get opaque =&gt; false;

  @override
  bool get barrierDismissible =&gt; false;

  @override
  Color get barrierColor =&gt; Colors.black;

  @override
  String? get barrierLabel =&gt; null;

  @override
  bool get maintainState =&gt; true;


  getSearch(String query) async {
    var result = await SearchService().getAllPaged(query: query);
    setState(() {
      searchResults = result;
    });
  }

  _onSearchChanged(String query) {
    getSearch(query);
  }

  @override
  Widget buildPage(
    BuildContext context,
    Animation&lt;double&gt; animation,
    Animation&lt;double&gt; secondaryAnimation,
  ) {
    return Scaffold(
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Expanded(
                    child: TextField(
                      onChanged: _onSearchChanged,
                      autofocus: true,
                    ),
                  ),
                  TextButton(
                      onPressed: () =&gt; Navigator.of(context).pop(),
                      child: const Text(&#39;Cancel&#39;))
                ],
              ),
              const SizedBox(
                height: 20,
              ),
              ...searchResults.map(
                (search) {
                  return ListTile(
                    title: Text(search.title),
                  );
                },
              )
            ],
          ),
        ),
      ),
    );
  }
}

答案1

得分: 1

Flutters setState()方法仅在StatefulWidget中可用,用于在状态更改时重建小部件。您不能从ModalRoute中调用setState()方法,因为它在那里不存在。

您可以使用ValueNotifierValueListenableBuilder来实现您的目标。

代码:

// 定义一个扩展ModalRoute的新类JobSearchModal
class JobSearchModal extends ModalRoute {
  // 声明一个ValueNotifier,每当搜索结果发生变化时,它将通知所有监听器
  final ValueNotifier<List<dynamic>> searchResults = ValueNotifier<List<dynamic>>([]);

  // 设置转场的持续时间
  @override
  Duration get transitionDuration => const Duration(milliseconds: 250);

  @override
  bool get opaque => false;

  @override
  bool get barrierDismissible => false;

  @override
  Color get barrierColor => Colors.black;

  @override
  String? get barrierLabel => null;

  @override
  bool get maintainState => true;

  // 定义此异步函数以获取搜索结果
  getSearch(String query) async {
    var result = await SearchService().getAllPaged(query: query);
    searchResults.value = result;
  }

  // 定义在搜索查询更改时执行的函数
  _onSearchChanged(String query) {
    getSearch(query);
  }

  @override
  Widget buildPage(
    BuildContext context,
    Animation<double> animation,
    Animation<double> secondaryAnimation,
  ) {
    return Scaffold(
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Expanded(
                    child: TextField(
                      onChanged: _onSearchChanged,
                      autofocus: true,
                    ),
                  ),
                  TextButton(
                      onPressed: () => Navigator.of(context).pop(),
                      child: const Text('Cancel'))
                ],
              ),
              const SizedBox(
                height: 20,
              ),
              ValueListenableBuilder(
                valueListenable: searchResults,
                builder: (context, value, child) {
                  return Column(
                    children: value.map(
                      (search) {
                        return ListTile(
                          title: Text(search.title),
                        );
                      },
                    ).toList(),
                  );
                },
              )
            ],
          ),
        ),
      ),
    );
  }
}

(Note: I've removed HTML encoding for special characters like &lt; and &gt; in your code.)

英文:

Flutters setState() method is only available within a StatefulWidget to rebuild the widgets when there is a state change. You can not call setState() method from ModalRoute since it doesn't exist there.

You could use ValueNotifier and ValueListenableBuilder to achieve your goal.

The Code:

// Define a new class JobSearchModal that extends ModalRoute
class JobSearchModal extends ModalRoute {
  // Declare a ValueNotifier which will notify all the listeners whenever there&#39;s a change in search results
  final ValueNotifier&lt;List&lt;dynamic&gt;&gt; searchResults = ValueNotifier&lt;List&lt;dynamic&gt;&gt;([]);

  // Set duration of transition
  @override
  Duration get transitionDuration =&gt; const Duration(milliseconds: 250);

  @override
  bool get opaque =&gt; false;

  @override
  bool get barrierDismissible =&gt; false;

  @override
  Color get barrierColor =&gt; Colors.black;

  @override
  String? get barrierLabel =&gt; null;

  @override
  bool get maintainState =&gt; true;

  // Define this asynchronous function to get search results
  getSearch(String query) async {
    var result = await SearchService().getAllPaged(query: query);
    searchResults.value = result;
  }

  // Define a function that gets executed when a search query changes
  _onSearchChanged(String query) {
    getSearch(query);
  }

  @override
  Widget buildPage(
    BuildContext context,
    Animation&lt;double&gt; animation,
    Animation&lt;double&gt; secondaryAnimation,
  ) {
    return Scaffold(
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Expanded(
                    child: TextField(
                      onChanged: _onSearchChanged,
                      autofocus: true,
                    ),
                  ),
                  TextButton(
                      onPressed: () =&gt; Navigator.of(context).pop(),
                      child: const Text(&#39;Cancel&#39;))
                ],
              ),
              const SizedBox(
                height: 20,
              ),
              ValueListenableBuilder(
                valueListenable: searchResults,
                builder: (context, value, child) {
                  return Column(
                    children: value.map(
                      (search) {
                        return ListTile(
                          title: Text(search.title),
                        );
                      },
                    ).toList(),
                  );
                },
              )
            ],
          ),
        ),
      ),
    );
  }
}

答案2

得分: 0

StatefulBuilder(
builder: (BuildContext context, setState) {
return ;
},
),

Use StatefulBuilder

Example:

await showDialog<void>(
context: context,
builder: (BuildContext context) {
int? selectedRadio = 0;
return AlertDialog(
content: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Column(
mainAxisSize: MainAxisSize.min,
children: List<Widget>.generate(4, (int index) {
return Radio<int>(
value: index,
groupValue: selectedRadio,
onChanged: (int? value) {
setState(() => selectedRadio = value);
},
);
}),
);
},
),
);
},
);

英文:
 StatefulBuilder(
builder: (BuildContext context, setState) {
return ;
},
),

Use StatefulBuilder

Example:

await showDialog&lt;void&gt;(
context: context,
builder: (BuildContext context) {
int? selectedRadio = 0;
return AlertDialog(
content: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Column(
mainAxisSize: MainAxisSize.min,
children: List&lt;Widget&gt;.generate(4, (int index) {
return Radio&lt;int&gt;(
value: index,
groupValue: selectedRadio,
onChanged: (int? value) {
setState(() =&gt; selectedRadio = value);
},
);
}),
);
},
),
);
},
);

huangapple
  • 本文由 发表于 2023年6月26日 17:53:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/76555549.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定