我想创建一个TextField来检查数据库中是否存在该值。
如何使用带有TextField
小部件的BLOC模式进行异步验证?
我应该使用StreamTransformer
向Stream
添加错误吗?我尝试使用DebounceStreamTransformer
,但这只是阻止Stream
接收新值。
这是我的Observable
Observable<String> get valueStream => valueController.stream.transform(PropertyNameExist.handle('Blabla', null));
这是我的StreamTransformer
class PropertyNameExist implements StreamTransformerValidator {
static StreamTransformer<String, String> handle(String fieldname, String data) {
Http http = new Http();
return StreamTransformer<String, String>.fromHandlers(
handleData: (String stringData, sink) {
http.post('/my_api',data:{
'property_name':stringData,
}).then((Response response){
Map<String,dynamic> responseData = jsonDecode(response.data);
bool isValid = responseData['valid'] == 'true';
if(isValid){
sink.add(stringData);
} else {
sink.addError('Opps Error');
}
});
});
}
}
这是我的Widget
StreamBuilder<String>(
stream: valueStream,
builder: (context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
_textInputController.setTextAndPosition(snapshot.data);
}
return TextField(
controller: _textInputController,
onChanged: (String newVal) {
updateValue(newVal);
},
decoration: InputDecoration(
errorText: snapshot.error,
),
);
},
)
答案 0 :(得分:3)
您可能不再在寻找解决方案,但是基于问题的支持,我仍然想提供答案。
我不确定我是否正确理解您的代码,并且看起来您是在自己实现BLoC,所以这是一个免责声明,因为我正在提供一个使用Felix Angelov(pub.dev/packages/bloc的BLoC实现的解决方案。 )。
以下代码的结果
代码和方法:
首先,我创建了一个空项目,并添加了BLoC库;我在pubspec.yaml
中添加了
flutter_bloc: ^3.2.0
然后我创建了一个新的BackendValidationBloc
团体,其中有一个事件ValidateInput
并具有多种状态,如以下代码段所示。
事件代码:
在大多数情况下,我从定义事件开始,这在我的示例中非常简单:
part of 'backend_validation_bloc.dart';
@immutable
abstract class BackendValidationEvent {}
class ValidateInput extends BackendValidationEvent {
final String input;
ValidateInput({@required this.input});
}
州代码:
然后,您可能想要一个具有多个属性或多个状态的状态。我决定使用具有多个属性的一种状态,因为我认为在UI中更容易处理。在此示例中,我建议向用户提供反馈,因为通过后端验证输入可能需要一些时间。因此,BackendValidationState
具有两个状态:loading
和validated
。
part of 'backend_validation_bloc.dart';
@immutable
class BackendValidationState {
final bool isInProcess;
final bool isValidated;
bool get isError => errorMessage.isNotEmpty;
final String errorMessage;
BackendValidationState(
{this.isInProcess, this.isValidated, this.errorMessage});
factory BackendValidationState.empty() {
return BackendValidationState(
isInProcess: false, isValidated: false);
}
BackendValidationState copyWith(
{bool isInProcess, bool isValidated, String errorMessage}) {
return BackendValidationState(
isValidated: isValidated ?? this.isValidated,
isInProcess: isInProcess ?? this.isInProcess,
// This is intentionally not defined as
// errorMessage: errorMessage ?? this.errorMessage
// because if the errorMessage is null, it means the input was valid
errorMessage: errorMessage,
);
}
BackendValidationState loading() {
return this.copyWith(isInProcess: true);
}
BackendValidationState validated({@required String errorMessage}) {
return this.copyWith(errorMessage: errorMessage, isInProcess: false);
}
}
集团代码:
最后,通过定义对后端进行调用的块,将事件与状态“连接”起来:
import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:meta/meta.dart';
part 'backend_validation_event.dart';
part 'backend_validation_state.dart';
class BackendValidationBloc
extends Bloc<BackendValidationEvent, BackendValidationState> {
@override
BackendValidationState get initialState => BackendValidationState.empty();
@override
Stream<BackendValidationState> mapEventToState(
BackendValidationEvent event,
) async* {
if (event is ValidateInput) {
yield this.state.loading();
String backendValidationMessage =
await this.simulatedBackendFunctionality(event.input);
yield this.state.validated(errorMessage: backendValidationMessage);
}
}
Future<String> simulatedBackendFunctionality(String input) async {
// This simulates delay of the backend call
await Future.delayed(Duration(milliseconds: 500));
// This simulates the return of the backend call
String backendValidationMessage;
if (input != 'hello') {
backendValidationMessage = "Input does not equal to 'hello'";
}
return backendValidationMessage;
}
}
用户界面代码:
如果您不熟悉如何在UI中使用已实现的BLoC,则这是前端代码,使用状态将不同的值(用于实际的错误消息以及在等待后端响应时的用户反馈)提供给TextField
的errorText属性。
import 'package:backend_validation_using_bloc/bloc/backend_validation_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
void main() => runApp(App());
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: BlocProvider<BackendValidationBloc>(
create: (context) => BackendValidationBloc(), child: HomeScreen()),
);
}
}
class HomeScreen extends StatefulWidget {
HomeScreen({Key key}) : super(key: key);
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
TextEditingController textEditingController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: BlocBuilder<BackendValidationBloc, BackendValidationState>(
builder: (BuildContext context, BackendValidationState state) {
return TextField(
controller: textEditingController,
onChanged: (String currentValue) {
BlocProvider.of<BackendValidationBloc>(context)
.add(ValidateInput(input: currentValue));
},
decoration: InputDecoration(errorText: state.isInProcess ? 'Valiating input...' : state.errorMessage),
);
},
));
}
}
连接真实的后端
所以我有点伪造了一个后端,但是如果您想使用一个真实的后端,通常会实现一个Repository
并将其传递给构造函数中的BLoC
,这使得可以使用不同的实现后端更容易(如果针对接口正确实现)。如果您想要更详细的教程,请查看Felix Angelov's tutorials(它们非常不错)
希望这对您或其他人有帮助。