From dc04a880ef9a39a7e6478d661c14afa1c181dd8b Mon Sep 17 00:00:00 2001 From: violet-dev Date: Sun, 5 Jan 2025 10:27:18 +0900 Subject: [PATCH 1/3] Fix message search result parsing error --- violet/lib/pages/lab/lab/search_message.dart | 32 ++++++------------- .../lib/pages/viewer/viewer_controller.dart | 2 +- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/violet/lib/pages/lab/lab/search_message.dart b/violet/lib/pages/lab/lab/search_message.dart index df78748cf..b0f1cb909 100644 --- a/violet/lib/pages/lab/lab/search_message.dart +++ b/violet/lib/pages/lab/lab/search_message.dart @@ -2,15 +2,14 @@ // Copyright (C) 2020-2024. violet-team. Licensed under the Apache-2.0 License. import 'dart:async'; -import 'dart:convert'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:violet/component/hentai.dart'; +import 'package:violet/component/hitomi/message_search.dart'; import 'package:violet/component/hitomi/tag_translate.dart'; import 'package:violet/component/image_provider.dart'; -import 'package:violet/network/wrapper.dart' as http; import 'package:violet/other/dialogs.dart'; import 'package:violet/pages/common/utils.dart'; import 'package:violet/pages/lab/lab/search_message_rank.dart'; @@ -34,7 +33,6 @@ class _LabSearchMessageState extends State { <(double, int, int, double, List)>[]; TextEditingController text = TextEditingController(text: '은근슬쩍'); String latestSearch = '은근슬쩍'; - List<(String, String, int)>? autocompleteTarget; @override void initState() { @@ -43,9 +41,10 @@ class _LabSearchMessageState extends State { Future.delayed(const Duration(milliseconds: 100)).then((value) async { var tmessages = (await VioletServer.searchMessage('contains', text.text)) as List; + messages = tmessages .map((e) => ( - double.parse(e['MatchScore'] as String), + e['MatchScore'] as double, e['Id'] as int, e['Page'] as int, e['Correctness'] as double, @@ -64,20 +63,7 @@ class _LabSearchMessageState extends State { await ScriptManager.refresh(); - setState(() {}); - }); - - Future.delayed(const Duration(milliseconds: 100)).then((value) async { - const url = - 'https://raw.githubusercontent.com/project-violet/violet-message-search/master/SORT-COMBINE.json'; - - var m = jsonDecode((await http.get(url)).body) as Map; - - autocompleteTarget = m.entries - .map((e) => (e.key, TagTranslate.disassembly(e.key), e.value as int)) - .toList(); - - autocompleteTarget!.sort((x, y) => y.$3.compareTo(x.$3)); + await MessageSearch.init(); setState(() {}); }); @@ -296,7 +282,7 @@ class _LabSearchMessageState extends State { selected.toLowerCase(), text.text)) as List; messages = tmessages .map((e) => ( - double.parse(e['MatchScore'] as String), + e['MatchScore'] as double, e['Id'] as int, e['Page'] as int, e['Correctness'] as double, @@ -321,16 +307,16 @@ class _LabSearchMessageState extends State { Expanded( child: TypeAheadField( suggestionsCallback: (pattern) async { - if (autocompleteTarget == null) { + if (MessageSearch.autocompleteTarget.isEmpty) { return <(String, String, int)>[]; } var ppattern = TagTranslate.disassembly(pattern); - return autocompleteTarget! + return MessageSearch.autocompleteTarget .where((element) => element.$2.startsWith(ppattern)) .toList() - ..addAll(autocompleteTarget! + ..addAll(MessageSearch.autocompleteTarget .where((element) => !element.$2.startsWith(ppattern) && element.$2.contains(ppattern)) @@ -414,7 +400,7 @@ class _LabSearchMessageState extends State { as List; messages = tmessages .map((e) => ( - double.parse(e['MatchScore'] as String), + e['MatchScore'] as double, e['Id'] as int, e['Page'] as int, double.parse(e['Correctness'].toString()), diff --git a/violet/lib/pages/viewer/viewer_controller.dart b/violet/lib/pages/viewer/viewer_controller.dart index ec3972972..696423c49 100644 --- a/violet/lib/pages/viewer/viewer_controller.dart +++ b/violet/lib/pages/viewer/viewer_controller.dart @@ -288,7 +288,7 @@ class ViewerController extends GetxController { as List; messages = tmessages .map((e) => ( - double.parse(e['MatchScore'] as String), + e['MatchScore'] as double, e['Id'] as int, e['Page'] as int, double.parse(e['Correctness'].toString()), From 82a3f79582af8f26cccab9baf5548a35f97a9b4b Mon Sep 17 00:00:00 2001 From: violet-dev Date: Sun, 5 Jan 2025 10:43:11 +0900 Subject: [PATCH 2/3] Declare MessageSearchResult --- .../lib/component/hitomi/message_search.dart | 33 +++++++ violet/lib/pages/lab/lab/search_message.dart | 85 +++++-------------- .../pages/viewer/vertical_viewer_page.dart | 12 +-- .../lib/pages/viewer/viewer_controller.dart | 28 ++---- violet/lib/server/violet.dart | 21 ++--- 5 files changed, 80 insertions(+), 99 deletions(-) diff --git a/violet/lib/component/hitomi/message_search.dart b/violet/lib/component/hitomi/message_search.dart index d7077a57a..69e261b19 100644 --- a/violet/lib/component/hitomi/message_search.dart +++ b/violet/lib/component/hitomi/message_search.dart @@ -24,3 +24,36 @@ class MessageSearch { autocompleteTarget.sort((x, y) => y.$3.compareTo(x.$3)); } } + +class MessageSearchResult { + final double matchScore; + final int id; + final int page; + final double correctness; + final List rect; + + MessageSearchResult({ + required this.matchScore, + required this.id, + required this.page, + required this.correctness, + required this.rect, + }); + + static List fromJson(String json) { + final result = jsonDecode(json) as List; + return result + .map( + (e) => MessageSearchResult( + matchScore: e['MatchScore'] as double, + id: e['Id'] as int, + page: e['Page'] as int, + correctness: e['Correctness'] as double, + rect: (e['Rect'] as List) + .map((e) => double.parse(e.toString())) + .toList(), + ), + ) + .toList(); + } +} diff --git a/violet/lib/pages/lab/lab/search_message.dart b/violet/lib/pages/lab/lab/search_message.dart index b0f1cb909..e7abaf724 100644 --- a/violet/lib/pages/lab/lab/search_message.dart +++ b/violet/lib/pages/lab/lab/search_message.dart @@ -29,8 +29,7 @@ class LabSearchMessage extends StatefulWidget { } class _LabSearchMessageState extends State { - List<(double, int, int, double, List)> messages = - <(double, int, int, double, List)>[]; + List messages = []; TextEditingController text = TextEditingController(text: '은근슬쩍'); String latestSearch = '은근슬쩍'; @@ -39,20 +38,7 @@ class _LabSearchMessageState extends State { super.initState(); Future.delayed(const Duration(milliseconds: 100)).then((value) async { - var tmessages = (await VioletServer.searchMessage('contains', text.text)) - as List; - - messages = tmessages - .map((e) => ( - e['MatchScore'] as double, - e['Id'] as int, - e['Page'] as int, - e['Correctness'] as double, - (e['Rect'] as List) - .map((e) => double.parse(e.toString())) - .toList() - )) - .toList(); + messages = (await VioletServer.searchMessage('contains', text.text))!; if (_height == null) { _height = List.filled(messages.length, 0); @@ -105,18 +91,16 @@ class _LabSearchMessageState extends State { cacheExtent: height * 3.0, itemCount: messages.length, itemBuilder: (BuildContext ctxt, int index) { - // if (messages.length == 0) return Container(); - var e = messages[index]; - + final e = messages[index]; return FutureBuilder( future: Future.delayed(const Duration(milliseconds: 100)) .then((value) async { VioletImageProvider provider; - if (ProviderManager.isExists(e.$2)) { - provider = await ProviderManager.get(e.$2); + if (ProviderManager.isExists(e.id)) { + provider = await ProviderManager.get(e.id); } else { final query = - (await HentaiManager.idSearch(e.$2.toString())) + (await HentaiManager.idSearch(e.id.toString())) .results; provider = await HentaiManager.getImageProvider(query[0]); await provider.init(); @@ -124,8 +108,8 @@ class _LabSearchMessageState extends State { } return ( - _urls![index] = await provider.getImageUrl(e.$3), - await provider.getHeader(e.$3) + _urls![index] = await provider.getImageUrl(e.page), + await provider.getHeader(e.page) ); }), builder: (context, @@ -146,8 +130,8 @@ class _LabSearchMessageState extends State { ), ), ListTile( - title: Text('${e.$2} (${e.$3 + 1} Page)'), - subtitle: Text('Score: ${e.$1}'), + title: Text('${e.id} (${e.page + 1} Page)'), + subtitle: Text('Score: ${e.matchScore}'), ), ], ); @@ -155,7 +139,7 @@ class _LabSearchMessageState extends State { return InkWell( onTap: () async { FocusScope.of(context).unfocus(); - showArticleInfoById(context, e.$2); + showArticleInfoById(context, e.id); }, splashColor: Colors.white, child: Column( @@ -221,10 +205,10 @@ class _LabSearchMessageState extends State { (context, AsyncSnapshot snapshot2) { if (!snapshot2.hasData) return Container(); - var brtx = e.$5[0]; - var brty = e.$5[1]; - var brbx = e.$5[2]; - var brby = e.$5[3]; + var brtx = e.rect[0]; + var brty = e.rect[1]; + var brbx = e.rect[2]; + var brby = e.rect[3]; var w = snapshot2.data!.width; @@ -251,8 +235,8 @@ class _LabSearchMessageState extends State { ], ), ListTile( - title: Text('${e.$2} (${e.$3 + 1} Page)'), - subtitle: Text('Score: ${e.$1}'), + title: Text('${e.id} (${e.page + 1} Page)'), + subtitle: Text('Score: ${e.matchScore}'), ), ], ), @@ -273,24 +257,14 @@ class _LabSearchMessageState extends State { value: selected, onChanged: (String? value) async { if (value == selected) return; - messages = <(double, int, int, double, List)>[]; + messages.clear(); setState(() { selected = value!; }); - var tmessages = (await VioletServer.searchMessage( - selected.toLowerCase(), text.text)) as List; - messages = tmessages - .map((e) => ( - e['MatchScore'] as double, - e['Id'] as int, - e['Page'] as int, - e['Correctness'] as double, - (e['Rect'] as List) - .map((e) => double.parse(e.toString())) - .toList(), - )) - .toList(); + + messages = (await VioletServer.searchMessage( + 'contains', text.text))!; evictImageUrls(_urls); @@ -392,23 +366,10 @@ class _LabSearchMessageState extends State { Future _onModifiedText() async { if (latestSearch == text.text) return; latestSearch = text.text; - messages = <(double, int, int, double, List)>[]; + messages.clear(); setState(() {}); - var tmessages = - (await VioletServer.searchMessage(selected.toLowerCase(), text.text)) - as List; - messages = tmessages - .map((e) => ( - e['MatchScore'] as double, - e['Id'] as int, - e['Page'] as int, - double.parse(e['Correctness'].toString()), - (e['Rect'] as List) - .map((e) => double.parse(e.toString())) - .toList() - )) - .toList(); + messages = (await VioletServer.searchMessage('contains', text.text))!; evictImageUrls(_urls); diff --git a/violet/lib/pages/viewer/vertical_viewer_page.dart b/violet/lib/pages/viewer/vertical_viewer_page.dart index 87f721bc1..11973d922 100644 --- a/violet/lib/pages/viewer/vertical_viewer_page.dart +++ b/violet/lib/pages/viewer/vertical_viewer_page.dart @@ -411,19 +411,19 @@ class _VerticalViewerPageState extends State _isExistsMessageSearchLayer(int index) { return c.search.value && c.imgHeight[index] != 0 && - c.messages.where((element) => element.$3 == index).isNotEmpty; + c.messages.where((element) => element.page == index).isNotEmpty; } _messageSearchLayer(int index) { final ratio = c.imgHeight[index] / c.realImgHeight[index]; final messages = - c.messages.where((element) => element.$3 == index).toList(); + c.messages.where((element) => element.page == index).toList(); final boxes = messages.map((e) { - var brtx = e.$5[0]; - var brty = e.$5[1]; - var brbx = e.$5[2]; - var brby = e.$5[3]; + var brtx = e.rect[0]; + var brty = e.rect[1]; + var brbx = e.rect[2]; + var brby = e.rect[3]; return Positioned( top: brty * ratio - 4, diff --git a/violet/lib/pages/viewer/viewer_controller.dart b/violet/lib/pages/viewer/viewer_controller.dart index 696423c49..bc6833145 100644 --- a/violet/lib/pages/viewer/viewer_controller.dart +++ b/violet/lib/pages/viewer/viewer_controller.dart @@ -9,6 +9,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:get/get.dart'; +import 'package:violet/component/hitomi/message_search.dart'; import 'package:violet/pages/viewer/others/preload_page_view.dart'; import 'package:violet/pages/viewer/others/scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:violet/pages/viewer/viewer_page_provider.dart'; @@ -93,7 +94,7 @@ class ViewerController extends GetxController { SuggestionsBoxController? suggestionsBoxController; /// Is enabled search? - var messages = <(double, int, int, double, List)>[]; + var messages = []; String latestSearch = ''; var messageIndex = 0.obs; @@ -272,7 +273,7 @@ class ViewerController extends GetxController { } gotoSearchIndex() { - final index = messages[messageIndex.value - 1].$3; + final index = messages[messageIndex.value - 1].page; jump(index); } @@ -281,25 +282,10 @@ class ViewerController extends GetxController { suggestionsBoxController!.close(); if (latestSearch == searchText.text) return; latestSearch == searchText.text; - messages = <(double, int, int, double, List)>[]; - - final tmessages = - (await VioletServer.searchMessageWord(articleId, searchText.text)) - as List; - messages = tmessages - .map((e) => ( - e['MatchScore'] as double, - e['Id'] as int, - e['Page'] as int, - double.parse(e['Correctness'].toString()), - (e['Rect'] as List) - .map((e) => double.parse(e.toString())) - .toList() - )) - .toList(); - - messages = messages.where((e) => e.$1 >= 80.0).toList(); - messages.sort((a, b) => a.$3.compareTo(b.$3)); + messages = + (await VioletServer.searchMessageWord(articleId, searchText.text))!; + messages = messages.where((e) => e.matchScore >= 80.0).toList(); + messages.sort((a, b) => a.page.compareTo(b.page)); messageIndex.value = 1; diff --git a/violet/lib/server/violet.dart b/violet/lib/server/violet.dart index 6ced3794f..e64944fcc 100644 --- a/violet/lib/server/violet.dart +++ b/violet/lib/server/violet.dart @@ -6,6 +6,7 @@ import 'dart:typed_data'; import 'package:dio/dio.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:violet/component/hitomi/message_search.dart'; import 'package:violet/database/user/bookmark.dart'; import 'package:violet/database/user/record.dart'; import 'package:violet/log/log.dart'; @@ -416,45 +417,45 @@ class VioletServer { } } - static Future searchMessage(String type, String what) async { + static Future?> searchMessage( + String type, String what) async { final gg = await http.get( '${Settings.searchMessageAPI}/$type/${Uri.encodeFull(what)}', headers: _vwHeader(), ); if (gg.statusCode != 200) { - return gg.statusCode; + return null; } try { - final result = (jsonDecode(gg.body) as List); - return result; + return MessageSearchResult.fromJson(gg.body); } catch (e, st) { Logger.error('[API-searchMessage] E: $e\n' '$st'); - return 900; + return null; } } - static Future searchMessageWord(int articleId, String what) async { + static Future?> searchMessageWord( + int articleId, String what) async { final gg = await http.get( '${Settings.searchMessageAPI}/wcontains/$articleId/${Uri.encodeFull(what)}', headers: _vwHeader(), ); if (gg.statusCode != 200) { - return gg.statusCode; + return null; } try { - final result = (jsonDecode(gg.body) as List); - return result; + return MessageSearchResult.fromJson(gg.body); } catch (e, st) { Logger.error('[API-searchMessageWord] E: $e\n' '$st'); - return 900; + return null; } } From eff1e94f038af6fa9d8acb0eec45ec059cfb728f Mon Sep 17 00:00:00 2001 From: violet-dev Date: Sun, 5 Jan 2025 11:15:58 +0900 Subject: [PATCH 3/3] Refactor getImageProvider related code --- violet/lib/pages/bookmark/crop_bookmark.dart | 23 +++------- violet/lib/pages/common/utils.dart | 32 +++++++++---- violet/lib/pages/lab/lab/search_message.dart | 45 +++++-------------- .../article_list_item_widget_controller.dart | 14 +----- 4 files changed, 42 insertions(+), 72 deletions(-) diff --git a/violet/lib/pages/bookmark/crop_bookmark.dart b/violet/lib/pages/bookmark/crop_bookmark.dart index a5146d3a8..163674b8c 100644 --- a/violet/lib/pages/bookmark/crop_bookmark.dart +++ b/violet/lib/pages/bookmark/crop_bookmark.dart @@ -17,8 +17,6 @@ import 'package:material_design_icons_flutter/material_design_icons_flutter.dart import 'package:path_provider/path_provider.dart'; import 'package:provider/provider.dart'; import 'package:pull_down_button/pull_down_button.dart'; -import 'package:violet/component/hentai.dart'; -import 'package:violet/component/image_provider.dart'; import 'package:violet/database/user/bookmark.dart'; import 'package:violet/database/user/record.dart'; import 'package:violet/log/log.dart'; @@ -150,22 +148,11 @@ class _CropBookmarkPageState extends State { return FutureBuilder( future: Future.delayed(const Duration(milliseconds: 100)).then((value) async { - VioletImageProvider provider; - - if (ProviderManager.isExists(articleId)) { - provider = await ProviderManager.get(articleId); - } else { - final query = - (await HentaiManager.idSearch(articleId.toString())).results; - provider = await HentaiManager.getImageProvider(query[0]); - await provider.init(); - ProviderManager.insert(query[0].id(), provider); - } - - return ( - imagesUrlForEvict![index] = await provider.getImageUrl(page), - await provider.getHeader(page) - ); + final provider = await getImageProviderFromId(articleId); + final image = await provider.getImageUrl(page); + final header = await provider.getHeader(page); + imagesUrlForEvict![index] = image; + return (image, header); }), builder: (context, AsyncSnapshot<(String, Map)> snapshot) { diff --git a/violet/lib/pages/common/utils.dart b/violet/lib/pages/common/utils.dart index 51e1f3f0f..cfcc99ddf 100644 --- a/violet/lib/pages/common/utils.dart +++ b/violet/lib/pages/common/utils.dart @@ -44,15 +44,7 @@ Future showArticleInfoRaw({ queryResult = await HentaiManager.idQueryWeb('$id'); } - late final VioletImageProvider provider; - if (ProviderManager.isExists(id)) { - provider = await ProviderManager.get(id); - } else { - provider = await HentaiManager.getImageProvider(queryResult); - await provider.init(); - ProviderManager.insert(id, provider); - } - + final provider = await getImageProvider(queryResult); final thumbnail = await provider.getThumbnailUrl(); final headers = await provider.getHeader(0); @@ -100,3 +92,25 @@ Future showArticleInfoRaw({ }, ); } + +Future getImageProviderFromId(int id) async { + if (ProviderManager.isExists(id)) { + return await ProviderManager.get(id); + } + + final query = (await HentaiManager.idSearch(id.toString())).results; + return getImageProvider(query[0]); +} + +Future getImageProvider(QueryResult queryResult) async { + final id = queryResult.id(); + if (ProviderManager.isExists(id)) { + return await ProviderManager.get(id); + } + + final provider = await HentaiManager.getImageProvider(queryResult); + await provider.init(); + ProviderManager.insert(id, provider); + + return provider; +} diff --git a/violet/lib/pages/lab/lab/search_message.dart b/violet/lib/pages/lab/lab/search_message.dart index e7abaf724..428978c09 100644 --- a/violet/lib/pages/lab/lab/search_message.dart +++ b/violet/lib/pages/lab/lab/search_message.dart @@ -6,10 +6,8 @@ import 'dart:async'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart'; -import 'package:violet/component/hentai.dart'; import 'package:violet/component/hitomi/message_search.dart'; import 'package:violet/component/hitomi/tag_translate.dart'; -import 'package:violet/component/image_provider.dart'; import 'package:violet/other/dialogs.dart'; import 'package:violet/pages/common/utils.dart'; import 'package:violet/pages/lab/lab/search_message_rank.dart'; @@ -18,7 +16,6 @@ import 'package:violet/pages/segment/platform_navigator.dart'; import 'package:violet/script/script_manager.dart'; import 'package:violet/server/violet.dart'; import 'package:violet/util/evict_image_urls.dart'; -import 'package:violet/widgets/article_item/image_provider_manager.dart'; import 'package:violet/widgets/v_cached_network_image.dart'; class LabSearchMessage extends StatefulWidget { @@ -95,22 +92,12 @@ class _LabSearchMessageState extends State { return FutureBuilder( future: Future.delayed(const Duration(milliseconds: 100)) .then((value) async { - VioletImageProvider provider; - if (ProviderManager.isExists(e.id)) { - provider = await ProviderManager.get(e.id); - } else { - final query = - (await HentaiManager.idSearch(e.id.toString())) - .results; - provider = await HentaiManager.getImageProvider(query[0]); - await provider.init(); - ProviderManager.insert(query[0].id(), provider); - } + final provider = await getImageProviderFromId(e.id); + final image = await provider.getImageUrl(e.page); + final header = await provider.getHeader(e.page); + _urls![index] = image; - return ( - _urls![index] = await provider.getImageUrl(e.page), - await provider.getHeader(e.page) - ); + return (image, header); }), builder: (context, AsyncSnapshot<(String, Map)> snapshot) { @@ -258,22 +245,10 @@ class _LabSearchMessageState extends State { onChanged: (String? value) async { if (value == selected) return; messages.clear(); - setState(() { selected = value!; }); - - messages = (await VioletServer.searchMessage( - 'contains', text.text))!; - - evictImageUrls(_urls); - - _height = List.filled(messages.length, 0); - _keys = List.generate( - messages.length, (index) => GlobalKey()); - _urls = List.filled(messages.length, ''); - - setState(() {}); + _doSearch(); }, ), ), @@ -367,9 +342,13 @@ class _LabSearchMessageState extends State { if (latestSearch == text.text) return; latestSearch = text.text; messages.clear(); - setState(() {}); - messages = (await VioletServer.searchMessage('contains', text.text))!; + _doSearch(); + } + + _doSearch() async { + messages = + (await VioletServer.searchMessage(selected.toLowerCase(), text.text))!; evictImageUrls(_urls); diff --git a/violet/lib/widgets/article_item/article_list_item_widget_controller.dart b/violet/lib/widgets/article_item/article_list_item_widget_controller.dart index 847c3d9d9..bebfe38d2 100644 --- a/violet/lib/widgets/article_item/article_list_item_widget_controller.dart +++ b/violet/lib/widgets/article_item/article_list_item_widget_controller.dart @@ -8,13 +8,11 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:html_unescape/html_unescape.dart'; import 'package:intl/intl.dart'; -import 'package:violet/component/hentai.dart'; -import 'package:violet/component/image_provider.dart'; import 'package:violet/database/user/bookmark.dart'; import 'package:violet/database/user/record.dart'; import 'package:violet/model/article_list_item.dart'; +import 'package:violet/pages/common/utils.dart'; import 'package:violet/settings/settings.dart'; -import 'package:violet/widgets/article_item/image_provider_manager.dart'; class ArticleListItemWidgetController extends GetxController { final ArticleListItem articleListItem; @@ -130,15 +128,7 @@ class ArticleListItemWidgetController extends GetxController { } setProvider() async { - VioletImageProvider provider; - - if (!ProviderManager.isExists(articleListItem.queryResult.id())) { - provider = - await HentaiManager.getImageProvider(articleListItem.queryResult); - ProviderManager.insert(articleListItem.queryResult.id(), provider); - } else { - provider = await ProviderManager.get(articleListItem.queryResult.id()); - } + final provider = await getImageProvider(articleListItem.queryResult); thumbnail.value = await provider.getThumbnailUrl(); headers.value = await provider.getHeader(0);