我通过实现BLOC和RX Dart在我的应用程序上有一个文章搜索功能。我已经成功地根据成为搜索关键字的"标题"搜索文章,但当我错误地填写"标题"时,结果未发出错误,如"文章不存在/任何其他内容",并且根据关键字"标题",结果不是实时的的含义,而是在LogCat中获得如下错误:
E/flutter (25135): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: NoSuchMethodError: Class 'String' has no instance method 'forEach'.
E/flutter (25135): Receiver: "Data not found"
E/flutter (25135): Tried calling: forEach(Closure: (dynamic) => Null)
E/flutter (25135): #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:50:5)
E/flutter (25135): #1 new Articles.fromJson (package:vallery/src/models/articles/articles.dart:11:22)
E/flutter (25135): #2 ApiProvider.searchArticle (package:vallery/src/resources/api/api_provider.dart:65:23)
E/flutter (25135): <asynchronous suspension>
E/flutter (25135): #3 Repository.searchArticlesRepository (package:vallery/src/resources/repository/repository.dart:19:74)
E/flutter (25135): #4 SearchArticleBloc.searchArticleBloc (package:vallery/src/blocs/article/articles_search_bloc.dart:12:42)
E/flutter (25135): <asynchronous suspension>
E/flutter (25135): #5 EditableTextState._formatAndSetValue (package:flutter/src/widgets/editable_text.dart:1335:14)
E/flutter (25135): #6 EditableTextState.updateEditingValue (package:flutter/src/widgets/editable_text.dart:971:5)
E/flutter (25135): #7 _TextInputClientHandler._handleTextInputInvocation (package:flutter/src/services/text_input.dart:743:36)
E/flutter (25135): <asynchronous suspension>
E/flutter (25135): #8 MethodChannel._handleAsMethodCall (package:flutter/src/services/platform_channel.dart:397:55)
E/flutter (25135): <asynchronous suspension>
E/flutter (25135): #9 MethodChannel.setMethodCallHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:365:54)
E/flutter (25135): #10 _DefaultBinaryMessenger.handlePlatformMessage (package:flutter/src/services/binary_messenger.dart:110:33)
E/flutter (25135): <asynchronous suspension>
E/flutter (25135): #11 _invoke3.<anonymous closure> (dart:ui/hooks.dart:280:15)
E/flutter (25135): #12 _rootRun (dart:async/zone.dart:1124:13)
E/flutter (25135): #13 _CustomZone.run (dart:async/zone.dart:1021:19)
E/flutter (25135): #14 _CustomZone.runGuarded (dart:async/zone.dart:923:7)
E/flutter (25135): #15 _invoke3 (dart:ui/hooks.dart:279:10)
E/flutter (25135): #16 _dispatchPlatformMessage (dart:ui/hooks.dart:141:5)
这是一个模型:
class Articles {
int status;
List<Result> result;
Articles({this.status, this.result});
Articles.fromJson(Map<String, dynamic> json) {
status = json['status'];
if (json['result'] != null) {
result = new List<Result>();
json['result'].forEach((v) {
result.add(new Result.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['status'] = this.status;
if (this.result != null) {
data['result'] = this.result.map((v) => v.toJson()).toList();
}
return data;
}
}
class Result {
String idArtikel;
String title;
String sinopsis;
String content;
String createdDate;
String thumbnail;
Result(
{this.idArtikel,
this.title,
this.sinopsis,
this.content,
this.createdDate,
this.thumbnail});
Result.fromJson(Map<String, dynamic> json) {
idArtikel = json['id_artikel'];
title = json['title'];
sinopsis = json['sinopsis'];
content = json['content'];
createdDate = json['created_date'];
thumbnail = json['thumbnail'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id_artikel'] = this.idArtikel;
data['title'] = this.title;
data['sinopsis'] = this.sinopsis;
data['content'] = this.content;
data['created_date'] = this.createdDate;
data['thumbnail'] = this.thumbnail;
return data;
}
}
这是一个API:
class ApiProvider {
Client client = Client();
static final String baseUrl = 'link_api';
Future<Articles> searchArticle(String title) async {
final response = await client.post(baseUrl + 'search-artikel', body: {
"title" : title
});
if (response.statusCode == 200) {
return Articles.fromJson(json.decode(response.body));
} else {
throw Exception('Gagal ambil data article');
}
}
}
这是存储库:
class Repository {
final apiProvider = ApiProvider();
Future<Articles> searchArticlesRepository(String title) => apiProvider.searchArticle(title);
}
这是BLoC:
class SearchArticleBloc {
final repository = Repository();
final articleSearchFetcher = PublishSubject<Articles>();
Observable<Articles> get allSearchArticle => articleSearchFetcher.stream;
searchArticleBloc(String title) async {
Articles articles = await repository.searchArticlesRepository(title);
articleSearchFetcher.sink.add(articles);
}
dispose() {
articleSearchFetcher.close();
}
}
这是用户界面:
class SearchArticlePage extends StatefulWidget {
@override
_SearchArticlePageState createState() => _SearchArticlePageState();
}
class _SearchArticlePageState extends State<SearchArticlePage> {
final blocSearchArticle = SearchArticleBloc();
@override
void dispose() {
blocSearchArticle.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: color_white,
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
backgroundColor: color_white,
iconTheme: IconThemeData(
color: color_blue_bg, //change your color here
),
centerTitle: true,
floating: true,
pinned: true,
title: TextField(
autofocus: true,
style: TextStyle(fontSize: 17, color: color_blue_bg),
decoration: InputDecoration.collapsed(
hintText: "Article Name...",
hintStyle: TextStyle(fontSize: 17, color: color_blue_bg),
),
onChanged: blocSearchArticle.searchArticleBloc,
),
)
];
},
body: getListResult(),
),
);
}
Widget getListResult() {
return StreamBuilder(
stream: blocSearchArticle.allSearchArticle,
builder: (BuildContext context, AsyncSnapshot<Articles> snapshot) {
if(snapshot.hasData) {
return showListResult(snapshot);
} else if (!snapshot.hasData) {
return Center(
child: Text('Data not found'),
);
} else if(snapshot.hasError) {
return Text(snapshot.error.toString());
}
return Center(
child: CircularProgressIndicator(),
);
},
);
}
Widget showListResult(AsyncSnapshot<Articles> snapshot) {
return Container(
margin: EdgeInsets.only(top: 10.0),
child: ListView.builder(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
scrollDirection: Axis.vertical,
itemCount: snapshot.data.result == null ? Center(child: Text('Data not found')) : snapshot?.data?.result?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
child: Container(
height: MediaQuery.of(context).size.height / 7.0,
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Container(
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
child: FadeInImage.assetNetwork(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width / 3,
placeholder: 'assets/images/img_default_bg.png',
image: '${snapshot.data.result[0].thumbnail}',
fit: BoxFit.cover,
),
),
),
Expanded(
child: Container(
margin: EdgeInsets.only(
left: MediaQuery.of(context).size.width / 41,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(
right: MediaQuery.of(context).size.width/41,
bottom: MediaQuery.of(context).size.width/ 80,
),
child: Text(
snapshot.data.result[index].title,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(
color: Colors.black,
fontSize: MediaQuery.of(context).size.width / 25,
fontWeight: FontWeight.bold,
),
),
),
Padding(
padding: EdgeInsets.only(
right: MediaQuery.of(context).size.width/41,
bottom: MediaQuery.of(context).size.width/ 80,
),
child: Text(
snapshot.data.result[index].sinopsis,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.black,
fontSize: MediaQuery.of(context).size.width / 35,
fontWeight: FontWeight.normal,
),
),
),
SizedBox(height: MediaQuery.of(context).size.height / 50,
),
Padding(
padding: EdgeInsets.only(
right: MediaQuery.of(context).size.width/41,
bottom: MediaQuery.of(context).size.width/ 80,
),
child: Text(
snapshot.data.result[index].createdDate,
style: TextStyle(
color: Colors.black,
fontSize: MediaQuery.of(context).size.width / 35,
fontWeight: FontWeight.normal,
),
),
),
],
),
),
),
],
),
),
),
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context){
return ArticleDetailPage(
id: snapshot.data.result[index].idArtikel,
title: snapshot.data.result[index].title,
thumbnail: '${snapshot.data.result[index].thumbnail}',
);
}),
);
},
);
},
),
);
}
}
有人能帮我吗?因为我头晕目眩地想找到这个错误的解决方案:)
2条答案
按热度按时间fruv7luv1#
问题就在这里:
您希望JSON的
"result"
部分是一个列表,但它显然是一个字符串。检查实际的JSON文本,您可能会找到"result": "some result"
,而不是"result": [ ...
考虑编辑问题以显示实际的JSON。顺便说一句,有一个更简单的语法来完成上面代码片段中的操作(当然,在解决JSON数组与字符串的问题之前,这将给出相同的错误)。
bt1cpqcv2#
解决这个问题
if (json['result'] != '' && json['result'] != null) { result = new List<Result>(); json['result'].forEach((v) { result.add(new Result.fromJson(v));});}