如果我使用提供程序,如何在应用程序启动时调用方法?
Posted
技术标签:
【中文标题】如果我使用提供程序,如何在应用程序启动时调用方法?【英文标题】:How to call method at App start if I am using provider? 【发布时间】:2019-12-10 19:34:51 【问题描述】:我想在服务器启动时完成get
请求以获取我的应用程序的数据。我阅读了几个主题,这些主题描述了如何在构建小部件后运行方法。但它们都是描述provider
不使用时的情况。而且我不确定在小部件中执行此请求是否是个好主意。
我尝试了几种方法,但都没有成功。 这是我的代码:
void main() async
runApp(new MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(
home: ChangeNotifierProvider<TenderApiData>(
builder: (_) => TenderApiData(), child: HomePage()),
);
class HomePage extends StatelessWidget
@override
Widget build(BuildContext context)
return Scaffold(appBar: AppBar(), body: MainContainer());
class TenderApiData with ChangeNotifier
String access_token;
List<Map<String, String>> id_names;
String access_token_url = "https://...";
getApiKey() async // I need to call this method at app start up
var response = await http
.post(access_token_url, headers: "Accept": "application/json");
if (response.statusCode == 200)
access_token = json.decode(response.body)['access_token'];
notifyListeners();
class MyTestWidget extends StatefulWidget
MyTestWidgetState createState() => MyTestWidgetState();
class MyTestWidgetState extends State<MyTestWidget>
bool isKeyGetted = false;
// before I used this when I extracted data on click event.
// I am not sure that it's needed now
@override
void didChangeDependencies()
if (!isKeyGetted)
Provider.of<TenderApiData>(context).getApiKey();
isKeyGetted = !isKeyGetted;
super.didChangeDependencies();
@override
Widget build(BuildContext context)
if (!isKeyGetted)
Provider.of<TenderApiData>(context).getApiKey();
isKeyGetted = !isKeyGetted;
var result = Provider.of<TenderApiData>(context).access_token;
var test = Provider.of<TenderApiData>(context).id_names;
return Column(
children: <Widget>[
RaisedButton(
onPressed: Provider.of<TenderApiData>(context).getRegionsList,
child: Text("get regions"),
),
],
);
class MainContainer extends StatelessWidget
@override
Widget build(BuildContext context)
return Table(
children: [
TableRow(children: [
Row(
children: <Widget>[
Container(child: MyTestWidget()),
Container(child: Text("Regions"),),
Expanded(child: SelectRegions(), )
],
)
]),
TableRow(children: [
Row(
children: <Widget>[
Text("Label"),
Text("Value"),
],
)
]),
],
);
【问题讨论】:
【参考方案1】:您可以将TenderApiData
存储为MyApp
的成员,在MyApp
构造函数中进行启动调用并将现有实例传递给后代。
下面是它的外观:
class MyApp extends StatelessWidget
final TenderApiData _tenderApiData = TenderApiData();
MyApp()
_tenderApiData.getApiKey();
;
@override
Widget build(BuildContext context)
return MaterialApp(
home: ChangeNotifierProvider<TenderApiData>(
builder: (_) => _tenderApiData, child: HomePage()),
);
其他类将保持不变。
另一种选择是将TenderApiData
作为构造函数参数传递给MyApp
,使其更易于测试。
void main()
final TenderApiData tenderApiData = TenderApiData();
tenderApiData.getApiKey(); // call it here or in MyApp constructor - now it can be mocked and tested
runApp(MyApp(tenderApiData));
class MyApp extends StatelessWidget
final TenderApiData _tenderApiData;
MyApp(this._tenderApiData);
// ...
【讨论】:
谢谢!你的解决方案有什么好处吗?或者关于构造函数的另一个答案是完全等价的? 虽然我一直在思考这个问题,但我想出了另一个更干净的解决方案。最好将TenderApiData
实例作为参数传递给MyApp
。它将使代码更具可测试性。如果您有TenderApiData
的单个实例,另一种解决方案也可以使用。如果您多次创建它 - 每次创建它都会调用。此外,恕我直言,它增加了测试和调试的复杂性。
谢谢!但是如果我需要打电话怎么办: await _tenderApiData.getApiKey(); _tenderApiData.getRegionsList(); (在第一次强制之前无法运行第二种方法)。构造函数可以是async
吗?
不,构造函数必须是同步的。它可以启动期货,但不能await
获得结果。但是,可以在期货上使用 .then
安排链式调用
您能否在答案中提及它?【参考方案2】:
您可以在TenderApiData
上添加一个构造函数来触发自定义逻辑:
class TenderApiData with ChangeNotifier
TenderApiData()
// TODO: call `getApiKey`
【讨论】:
但是如果我需要第一个方法完成怎么办。构造函数中不能调用 await 方法。 没有什么能阻止你使用异步方法。 但是在构造函数中不可能调用await,因为构造函数不能是异步的。 方法可以异步调用await。有什么问题?【参考方案3】:您可以使用FutureProvider
值。
单独的方法 api 到服务(my_service.dart):
class MyService
Future<String> getApiKey() async
// I need to call this method at app start up
var response = await http
.post(access_token_url, headers: "Accept": "application/json");
if (response.statusCode == 200)
return json.decode(response.body)['access_token'];
然后从 MyApp 调用
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return FutureProvider<String>.value(
value: MyService().getApiKey(),
child: HomePage(),
);
在主页中:
class HomePage extends StatelessWidget
@override
Widget build(BuildContext context)
String token = Provider.of<String>(context);
return Scaffold();
【讨论】:
【参考方案4】:您也可以只使用布尔标志来运行该方法只运行一次。
提供者
import 'screen.abstract.dart';
class MyProvider with ChangeNotifier
_hasInitialized = false;
// This will only be run once, when called
fetchApiOnce()
if (_hasInitialized)
return;
_hasInitialized = true;
/* DO STUFF */
【讨论】:
以上是关于如果我使用提供程序,如何在应用程序启动时调用方法?的主要内容,如果未能解决你的问题,请参考以下文章
如何阻止我的 MFC 应用程序在启动时调用 OnFileNew()?
防止在首次启动 iOS 时调用 AFNetworking 可达性状态
javascript在启动时调用托管bean的方法,而不是在命令按钮单击时调用它们