我是Flutter BLoC的超级新手,我目前正在学习构建股票应用程序的视频教程,同时实现我自己的一些功能。我有下面的代码,每当用户更新新的时,我想更新_apiKey。在下面的代码中,我无法在每次用户更新新的时更新_apiKey。然而,获取输入的第一个API文本并在整个过程中使用它,直到应用程序重新启动,然后它将使用updated _apiKey。
import 'package:animations/animations.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../api/api.dart';
import '../components/components.dart';
import '../state/state.dart';
import 'widgets.dart';
class SearchWidget extends StatefulWidget {
const SearchWidget({Key? key}) : super(key: key);
@override
State<SearchWidget> createState() => SearchWidgetState();
}
class SearchWidgetState extends State<SearchWidget> {
String _apiKey = '';
String apiKey = '';
final _loginWidgetNotifier = ValueNotifier<bool>(true);
bool get _loginWidget => _loginWidgetNotifier.value;
set _loginWidget(bool value) {
_loginWidgetNotifier.value = value;
}
bool error = false;
@override
void initState() {
super.initState();
loadApiKey();
loadLoginState();
}
void enterApiKey() {
showModal(
configuration: NonDismissibleModalConfiguration(),
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: const Text('Enter API key'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
obscureText: true,
onChanged: (value) {
// You can save the API key in a state variable here
apiKey = value;
},
decoration: InputDecoration(
hintText: 'API key',
errorBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: error ? Colors.red : Colors.grey,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: error ? Colors.red : Colors.grey,
),
),
focusedErrorBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: error ? Colors.red : Colors.grey,
),
),
disabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: error ? Colors.red : Colors.grey,
),
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: error ? Colors.red : Colors.grey,
),
),
border: UnderlineInputBorder(
borderSide: BorderSide(
color: error ? Colors.red : Colors.grey,
),
),
),
),
],
),
actions: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
MaterialButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
MaterialButton(
child: const Text('OK'),
onPressed: () {
if (apiKey.isNotEmpty) {
setState(() {
_apiKey = apiKey;
_loginWidget = false;
error = false;
apiKey = '';
});
saveApiKey();
saveLoginState();
Navigator.of(context).pop();
} else {
setState(() {
error = true;
});
}
}),
],
),
],
);
},
);
},
);
}
void saveApiKey() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('FinnhubApiKey', _apiKey);
}
void loadApiKey() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_apiKey = prefs.getString('FinnhubApiKey') ?? '';
});
}
void saveLoginState() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool('LoginState', _loginWidget);
}
void loadLoginState() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_loginWidget = prefs.getBool('LoginState') ?? true;
});
}
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => AppStateCubit(Api(Api.buildDefaultHttpClient(_apiKey))),
child: ValueListenableBuilder(
valueListenable: _loginWidgetNotifier,
builder: (context, loginWidgetValue, child) {
return SingleChildScrollView(
child: _loginWidget
? Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: Colors.white,
),
height: MediaQuery.of(context).size.height / 1.5,
width: MediaQuery.of(context).size.width / 4.2,
child: Center(
child: TextButton.icon(
onPressed: () {
enterApiKey();
},
icon: const Icon(
Icons.account_circle_rounded,
color: Colors.black,
size: 42,
),
label: const Text(
'Stocks',
style: TextStyle(
color: Colors.black,
fontSize: 21,
fontWeight: FontWeight.bold,
),
),
),
),
)
: SearchFormWidget(
toggleLoginWidget: () {
setState(() {
_loginWidget = true; // switch to login widget
});
saveLoginState();
},
),
);
},
),
);
}
}
API类如下
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:goalgamer/models/models.dart';
class Api {
final Dio _dio;
Api(this._dio);
static Dio buildDefaultHttpClient(String apiKey) {
final dio = Dio();
dio.options.baseUrl = "https://finnhub.io/api";
dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
options.queryParameters.addAll({
"token": apiKey,
});
return handler.next(options);
},
));
return dio;
}
Future<List<Candle>> getCandles(GetCandlesRequest request) async {
try {
final response = await _dio.get<Map<String, dynamic>>(
"/v1/stock/candle",
queryParameters: request.toJson(),
);
final candlesPayload = CandlesPayload.fromJson(response.data!);
return candlesPayload.toCandles();
} catch (e, s) {
debugPrint("$e\n$s");
throw ApiException("An unknown error occurred");
}
}
}
class ApiException {
final String message;
ApiException(this.message);
}
appstate类如下所示
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:goalgamer/api/api.dart';
import 'package:goalgamer/api/resolution.dart';
import 'package:goalgamer/models/models.dart';
import 'package:goalgamer/state/app_state.dart';
class AppStateCubit extends Cubit<AppState> {
final Api _api;
AppStateCubit(this._api) : super(AppState.initial());
Future<List<Candle>?> loadCandles(
String symbol,
DateTime from,
DateTime to,
) async {
emit(state.copyWith(isLoading: true, hasError: false));
// Find the first resolution which results in less than 100 candles
final resolution = Resolution.values.firstWhere(
(r) {
final ms = to.millisecondsSinceEpoch - from.millisecondsSinceEpoch;
final resultingCandles = ms / r.duration.inMilliseconds;
return resultingCandles < 100;
},
orElse: () => Resolution.month,
);
try {
final request = GetCandlesRequest(resolution, to, from, symbol);
emit(state.copyWith(recentQuery: request));
final candles = await _api.getCandles(request);
emit(state.copyWith(
isLoading: false,
candles: candles,
currentSymbol: symbol,
errorMessage:
candles.isEmpty ? "No data could be found for your request" : null,
hasError: candles.isEmpty,
));
return candles;
} on ApiException catch (e) {
emit(state.copyWith(
isLoading: false,
hasError: true,
errorMessage: e.message,
));
}
return null;
}
}
1条答案
按热度按时间aurhwmvo1#
希望这能帮上忙。
更新
您必须监听Bloc更改的状态,例如SearchWidget的构建函数可能是: