如何在HookWidget中使用useStreamController? [英] how to use useStreamController in HookWidget?
本文介绍了如何在HookWidget中使用useStreamController?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
我对颤振钩和河舱(状态管理)不熟悉
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String _url = "https://owlbot.info/api/v4/dictionary/";
String _token = "ae7cbdfff57e548a4360348ee519123a741d8e3d";
TextEditingController _controller = TextEditingController();
StreamController _streamController;
Stream _stream;
Timer _debounce;
Future _search() async {
if (_controller.text == null || _controller.text.length == 0) {
_streamController.add(null);
return;
}
_streamController.add("waiting");
Response response = await get(_url + _controller.text.trim(),
headers: {"Authorization": "Token " + _token});
_streamController.add(json.decode(response.body));
}
@override
void initState() {
super.initState();
_streamController = StreamController();
_stream = _streamController.stream;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Flictionary"),
bottom: PreferredSize(
preferredSize: Size.fromHeight(48.0),
child: Row(
children: <Widget>[
Expanded(
child: Container(
margin: const EdgeInsets.only(left: 12.0, bottom: 8.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(24.0),
),
child: TextFormField(
onChanged: (String text) {
if (_debounce?.isActive ?? false) _debounce.cancel();
_debounce = Timer(const Duration(milliseconds: 1000), () {
_search();
});
},
controller: _controller,
decoration: InputDecoration(
hintText: "Search for a word",
contentPadding: const EdgeInsets.only(left: 24.0),
border: InputBorder.none,
),
),
),
),
IconButton(
icon: Icon(
Icons.search,
color: Colors.white,
),
onPressed: () {
_search();
},
)
],
),
),
),
body: Container(
margin: const EdgeInsets.all(8.0),
child: StreamBuilder(
stream: _stream,
builder: (BuildContext ctx, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Center(
child: Text("Enter a search word"),
);
}
if (snapshot.data == "waiting") {
return Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
itemCount: snapshot.data["definitions"].length,
itemBuilder: (BuildContext context, int index) {
return ListBody(
children: <Widget>[
Container(
color: Colors.grey[300],
child: ListTile(
leading: snapshot.data["definitions"][index]
["image_url"] ==
null
? null
: CircleAvatar(
backgroundImage: NetworkImage(snapshot
.data["definitions"][index]["image_url"]),
),
title: Text(_controller.text.trim() +
"(" +
snapshot.data["definitions"][index]["type"] +
")"),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
snapshot.data["definitions"][index]["definition"]),
)
],
);
},
);
},
),
),
);
}
}
我只想将上面的statefWidget转换为HookWidget,以及如何使用riverpod作为上面示例的状态代理。我知道一些钩子和河荚的基本知识,但我仍然对钩子、状态管理(河荚)感到困惑。 请帮助理解它们,并提供一些示例,或者至少将上面的代码转换为钩子小部件,并使用HookBuilder
提前谢谢
推荐答案
首先,代码:
final textProvider = StateProvider<String>((_) => '');
final responseFutureProvider =
FutureProvider.autoDispose.family<Response, String>((ref, text) async {
if (text == null || text.length == 0) {
throw Error();
}
final String _url = "https://owlbot.info/api/v4/dictionary/";
final String _token = "ae7cbdfff57e548a4360348ee519123a741d8e3d";
return await get(_url + text.trim(), headers: {"Authorization": "Token " + _token});
});
final responseProvider = Computed<AsyncValue<Response>>((read) {
final text = read(textProvider).state;
return read(responseFutureProvider(text));
});
String _useDebouncedSearch(TextEditingController controller) {
final search = useState(controller.text);
useEffect(() {
Timer? timer;
void listener() {
timer?.cancel();
timer = Timer(
const Duration(milliseconds: 1000),
() => search.value = controller.text,
);
}
controller.addListener(listener);
return () {
timer?.cancel();
controller.removeListener(listener);
};
}, [controller]);
return search.value;
}
class MyHomePage extends HookWidget {
const MyHomePage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
final controller = useTextEditingController();
final text = useProvider(textProvider);
text.state = _useDebouncedSearch(controller);
return Scaffold(
appBar: AppBar(
title: Text("Flictionary"),
bottom: PreferredSize(
preferredSize: Size.fromHeight(48.0),
child: Row(
children: <Widget>[
Expanded(
child: Container(
margin: const EdgeInsets.only(left: 12.0, bottom: 8.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(24.0),
),
child: TextFormField(
controller: controller,
decoration: InputDecoration(
hintText: "Search for a word",
contentPadding: const EdgeInsets.only(left: 24.0),
border: InputBorder.none,
),
),
),
),
Icon(
Icons.search,
color: Colors.white,
),
],
),
),
),
body: MyHomePageBody(),
);
}
}
class MyHomePageBody extends HookWidget {
const MyHomePageBody({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
final text = useProvider(textProvider).state;
final response = useProvider(responseProvider);
response.when(
error: (err, stack) => Center(child: Text('Error: $err')),
loading: () => Center(child: CircularProgressIndicator()),
data: (response) => ListView.builder(
itemCount: Response["definitions"].length,
itemBuilder: (BuildContext context, int index) {
return ListBody(
children: <Widget>[
Container(
color: Colors.grey[300],
child: ListTile(
leading: response["definitions"][index]["image_url"] == null
? null
: CircleAvatar(
backgroundImage:
NetworkImage(response["definitions"][index]["image_url"]),
),
title: Text(text.trim() + "(" + response["definitions"][index]["type"] + ")"),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(response["definitions"][index]["definition"]),
)
],
);
},
),
);
}
}
- 我们添加外部文本提供程序,以便可以从其他提供程序读取文本字段。
- 我们创建了一个FutureProviderFamily,以便可以使用来自文本字段的文本参数执行API调用。在Riverpod中,家族启用向提供者传递参数。
- 我们创建了一个计算器,每当文本提供程序的值发生更改时,该计算器将调用Future。这将返回一个AsyncValue,它是您正在使用的StreamBuilder的完美替代(将介绍更多内容)。
- 稍微重构了去掉的搜索,以使用useEffect挂钩。这将处理为您的计时器释放资源,并在必要时更新extProvider。(我从Remi's Marvel example了解到这一点)
- 我们不再需要按下onChanged或手动按钮进行搜索,因为每当控制器更改时,文本提供程序的状态都会更新。
- 将页面正文移动到其自己的类中,以将需要加载的内容与静态内容分开。
- 现在,我们可以使用AsyncValue来处理生成的加载、错误和成功状态,而不是使用StreamBuilder。
我知道要介绍的内容很多,因此我建议深入阅读文档,以更多地了解本例中的所有内容。
这篇关于如何在HookWidget中使用useStreamController?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文