currency loading performance increase

This commit is contained in:
julian 2023-02-07 11:10:35 -06:00
parent 842593d6b2
commit 2366c40dcd

View file

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:isar/isar.dart'; import 'package:isar/isar.dart';
@ -14,6 +16,7 @@ import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/background.dart'; import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart'; import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart'; import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/loading_indicator.dart'; import 'package:stackwallet/widgets/loading_indicator.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart';
@ -47,11 +50,82 @@ class _ExchangeCurrencySelectionViewState
final _searchFocusNode = FocusNode(); final _searchFocusNode = FocusNode();
final isDesktop = Util.isDesktop; final isDesktop = Util.isDesktop;
late List<Currency> _currencies; List<Currency> _currencies = [];
late final List<Pair> pairs; List<Pair> pairs = [];
List<Pair> getAvailablePairs() { bool _loaded = false;
final filter = ExchangeDataLoadingService.instance.isar.pairs String _searchString = "";
Future<T> _showUpdatingCurrencies<T>({
required Future<T> whileFuture,
}) async {
unawaited(
showDialog<void>(
context: context,
barrierDismissible: false,
builder: (_) => WillPopScope(
onWillPop: () async => false,
child: Container(
color: Theme.of(context)
.extension<StackColors>()!
.overlay
.withOpacity(0.6),
child: const CustomLoadingOverlay(
message: "Loading currencies",
eventBus: null,
),
),
),
),
);
final result = await whileFuture;
if (mounted) {
Navigator.of(context, rootNavigator: isDesktop).pop();
}
return result;
}
Future<List<Currency>> _loadCurrencies() async {
if (widget.paired == null) {
return await _getCurrencies();
}
final pairs = await _loadAvailablePairs();
List<Currency> currencies = [];
for (final pair in pairs) {
final currency =
await _getCurrency(widget.willChangeIsSend ? pair.from : pair.to);
if (currency != null) {
currencies.add(currency);
}
}
return currencies;
}
Future<Currency?> _getCurrency(String ticker) {
return ExchangeDataLoadingService.instance.isar.currencies
.where()
.exchangeNameEqualTo(widget.exchangeName)
.filter()
.tickerEqualTo(ticker, caseSensitive: false)
.group((q) => widget.isFixedRate
? q
.rateTypeEqualTo(SupportedRateType.both)
.or()
.rateTypeEqualTo(SupportedRateType.fixed)
: q
.rateTypeEqualTo(SupportedRateType.both)
.or()
.rateTypeEqualTo(SupportedRateType.estimated))
.findFirst();
}
Future<List<Pair>> _loadAvailablePairs() {
final query = ExchangeDataLoadingService.instance.isar.pairs
.where() .where()
.exchangeNameEqualTo(widget.exchangeName) .exchangeNameEqualTo(widget.exchangeName)
.filter() .filter()
@ -63,89 +137,62 @@ class _ExchangeCurrencySelectionViewState
: q : q
.rateTypeEqualTo(SupportedRateType.both) .rateTypeEqualTo(SupportedRateType.both)
.or() .or()
.rateTypeEqualTo(SupportedRateType.estimated)); .rateTypeEqualTo(SupportedRateType.estimated))
.and()
.group((q) => widget.willChangeIsSend
? q.toEqualTo(widget.paired!.ticker, caseSensitive: false)
: q.fromEqualTo(widget.paired!.ticker, caseSensitive: false));
if (widget.paired != null) { if (widget.willChangeIsSend) {
return filter return query.sortByFrom().findAll();
.and()
.group((q) => widget.willChangeIsSend
? q.toEqualTo(widget.paired!.ticker, caseSensitive: false)
: q.fromEqualTo(widget.paired!.ticker, caseSensitive: false))
.findAllSync();
} else { } else {
return filter.findAllSync(); return query.sortByTo().findAll();
} }
} }
void filter(String text) { Future<List<Currency>> _getCurrencies() async {
setState(() { return ExchangeDataLoadingService.instance.isar.currencies
final query = ExchangeDataLoadingService.instance.isar.currencies .where()
.where() .exchangeNameEqualTo(widget.exchangeName)
.exchangeNameEqualTo(widget.exchangeName) .filter()
.filter() .group((q) => widget.isFixedRate
.anyOf<String, Currency>( ? q
pairs.map((e) => widget.willChangeIsSend ? e.to : e.from), .rateTypeEqualTo(SupportedRateType.both)
(q, ticker) => q.tickerEqualTo(ticker), .or()
) .rateTypeEqualTo(SupportedRateType.fixed)
.group((q) => widget.isFixedRate : q
? q .rateTypeEqualTo(SupportedRateType.both)
.rateTypeEqualTo(SupportedRateType.both) .or()
.or() .rateTypeEqualTo(SupportedRateType.estimated))
.rateTypeEqualTo(SupportedRateType.fixed) .sortByIsStackCoin()
: q .thenByName()
.rateTypeEqualTo(SupportedRateType.both) .findAll();
.or() }
.rateTypeEqualTo(SupportedRateType.estimated))
.and()
.group((q) => q
.nameContains(text, caseSensitive: false)
.or()
.tickerContains(text, caseSensitive: false));
if (widget.paired != null) { List<Currency> filter(String text) {
_currencies = query if (text.isEmpty) {
.and() return _currencies;
.not() }
.tickerEqualTo(widget.paired!.ticker)
.sortByIsStackCoin() if (widget.paired == null) {
.thenByTicker() return _currencies
.findAllSync(); .where((e) =>
} else { e.name.toLowerCase().contains(text.toLowerCase()) ||
_currencies = query.sortByIsStackCoin().thenByTicker().findAllSync(); e.ticker.toLowerCase().contains(text.toLowerCase()))
} .toList(growable: false);
}); } else {
return _currencies
.where((e) =>
e.ticker.toLowerCase() != widget.paired!.ticker.toLowerCase() &&
(e.name.toLowerCase().contains(text.toLowerCase()) ||
e.ticker.toLowerCase().contains(text.toLowerCase())))
.toList(growable: false);
}
} }
@override @override
void initState() { void initState() {
_searchController = TextEditingController(); _searchController = TextEditingController();
pairs = getAvailablePairs();
final query = ExchangeDataLoadingService.instance.isar.currencies
.where()
.exchangeNameEqualTo(widget.exchangeName)
.filter()
.group((q) => widget.isFixedRate
? q
.rateTypeEqualTo(SupportedRateType.both)
.or()
.rateTypeEqualTo(SupportedRateType.fixed)
: q
.rateTypeEqualTo(SupportedRateType.both)
.or()
.rateTypeEqualTo(SupportedRateType.estimated));
if (widget.paired != null) {
_currencies = query
.and()
.not()
.tickerEqualTo(widget.paired!.ticker)
.sortByIsStackCoin()
.thenByTicker()
.findAllSync();
} else {
_currencies = query.sortByIsStackCoin().thenByTicker().findAllSync();
}
super.initState(); super.initState();
} }
@ -159,6 +206,15 @@ class _ExchangeCurrencySelectionViewState
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (!_loaded) {
_loaded = true;
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
_currencies =
await _showUpdatingCurrencies(whileFuture: _loadCurrencies());
setState(() {});
});
}
return ConditionalParent( return ConditionalParent(
condition: !isDesktop, condition: !isDesktop,
builder: (child) { builder: (child) {
@ -211,7 +267,7 @@ class _ExchangeCurrencySelectionViewState
enableSuggestions: !isDesktop, enableSuggestions: !isDesktop,
controller: _searchController, controller: _searchController,
focusNode: _searchFocusNode, focusNode: _searchFocusNode,
onChanged: filter, onChanged: (value) => setState(() => _searchString = value),
style: STextStyles.field(context), style: STextStyles.field(context),
decoration: standardInputDecoration( decoration: standardInputDecoration(
"Search", "Search",
@ -241,8 +297,8 @@ class _ExchangeCurrencySelectionViewState
onTap: () async { onTap: () async {
setState(() { setState(() {
_searchController.text = ""; _searchController.text = "";
_searchString = "";
}); });
filter("");
}, },
), ),
], ],
@ -265,8 +321,12 @@ class _ExchangeCurrencySelectionViewState
), ),
Flexible( Flexible(
child: Builder(builder: (context) { child: Builder(builder: (context) {
final items = _currencies final coins = Coin.values.where((e) =>
.where((e) => Coin.values e.ticker.toLowerCase() !=
widget.paired?.ticker.toLowerCase());
final items = filter(_searchString)
.where((e) => coins
.where((coin) => .where((coin) =>
coin.ticker.toLowerCase() == e.ticker.toLowerCase()) coin.ticker.toLowerCase() == e.ticker.toLowerCase())
.isNotEmpty) .isNotEmpty)
@ -358,79 +418,82 @@ class _ExchangeCurrencySelectionViewState
height: 12, height: 12,
), ),
Flexible( Flexible(
child: RoundedWhiteContainer( child: Builder(builder: (context) {
padding: const EdgeInsets.all(0), final filtered = filter(_searchString);
child: ListView.builder( return RoundedWhiteContainer(
shrinkWrap: true, padding: const EdgeInsets.all(0),
primary: isDesktop ? false : null, child: ListView.builder(
itemCount: _currencies.length, shrinkWrap: true,
itemBuilder: (builderContext, index) { primary: isDesktop ? false : null,
final bool hasImageUrl = itemCount: filtered.length,
_currencies[index].image.startsWith("http"); itemBuilder: (builderContext, index) {
return Padding( final bool hasImageUrl =
padding: const EdgeInsets.symmetric(vertical: 4), filtered[index].image.startsWith("http");
child: GestureDetector( return Padding(
onTap: () { padding: const EdgeInsets.symmetric(vertical: 4),
Navigator.of(context).pop(_currencies[index]); child: GestureDetector(
}, onTap: () {
child: RoundedWhiteContainer( Navigator.of(context).pop(filtered[index]);
child: Row( },
children: [ child: RoundedWhiteContainer(
SizedBox( child: Row(
width: 24, children: [
height: 24, SizedBox(
child: isStackCoin(_currencies[index].ticker) width: 24,
? getIconForTicker( height: 24,
_currencies[index].ticker, child: isStackCoin(filtered[index].ticker)
size: 24, ? getIconForTicker(
) filtered[index].ticker,
: hasImageUrl size: 24,
? SvgPicture.network( )
_currencies[index].image, : hasImageUrl
width: 24, ? SvgPicture.network(
height: 24, filtered[index].image,
placeholderBuilder: (_) => width: 24,
const LoadingIndicator(), height: 24,
) placeholderBuilder: (_) =>
: const SizedBox( const LoadingIndicator(),
width: 24, )
height: 24, : const SizedBox(
), width: 24,
), height: 24,
const SizedBox( ),
width: 10,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_currencies[index].name,
style: STextStyles.largeMedium14(context),
),
const SizedBox(
height: 2,
),
Text(
_currencies[index].ticker.toUpperCase(),
style: STextStyles.smallMed12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
), ),
), const SizedBox(
], width: 10,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
filtered[index].name,
style: STextStyles.largeMedium14(context),
),
const SizedBox(
height: 2,
),
Text(
filtered[index].ticker.toUpperCase(),
style: STextStyles.smallMed12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
),
),
],
),
), ),
), ),
), );
); },
}, ),
), );
), }),
), ),
], ],
), ),