import 'package:flutter/material.dart'; import '../../../../bloc/theme/theme_bloc.dart'; import '../../../../data/radicals.dart'; import '../../../../routing/routes.dart'; import '../../../../services/jisho_api/radicals_search.dart'; class KanjiRadicalSearch extends StatefulWidget { const KanjiRadicalSearch({Key? key}) : super(key: key); @override _KanjiRadicalSearchState createState() => _KanjiRadicalSearchState(); } class _KanjiRadicalSearchState extends State { static const double fontSize = 25; List suggestions = []; Map radicalToggles = { for (final String r in radicals.values.expand((l) => l)) r: false }; Map allowedToggles = { for (final String r in radicals.values.expand((l) => l)) r: true }; void resetRadicalToggles() => radicalToggles.forEach((k, _) { radicalToggles[k] = false; }); void resetAllowedToggles() => allowedToggles.forEach((k, _) { allowedToggles[k] = true; }); Future updateSuggestions() async { final toggledRadicals = radicalToggles.keys.where((r) => radicalToggles[r] ?? false).toList(); if (toggledRadicals.isEmpty) { suggestions.clear(); resetAllowedToggles(); return; } final newSuggestions = await searchKanjiByRadicals(toggledRadicals); setState(() { allowedToggles.forEach((key, value) { allowedToggles[key] = false; }); for (final r in newSuggestions.validRadicals) { allowedToggles[r] = true; } suggestions = (newSuggestions.kanji ..sort((a, b) => a.strokes.compareTo(b.strokes))) .map((k) => k.kanji) .toList(); }); } Widget radicalGridElement(String radical, {bool isNumber = false}) { final color = isNumber ? LightTheme.defaultMenuGreyDark : radicalToggles[radical]! ? AppTheme.jishoGreen : LightTheme.defaultMenuGreyNormal; return InkWell( onTap: isNumber ? () {} : () => setState(() { // TODO: Don't let the user toggle on another kanji before the last one is updated radicalToggles[radical] = !radicalToggles[radical]!; updateSuggestions(); }), child: Container( alignment: Alignment.center, decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(5)), color: color.background, ), child: Text( radical, style: TextStyle( color: color.foreground, fontSize: fontSize, ), ), ), ); } List get radicalGridElements => [ IconButton( onPressed: () => setState(() { suggestions.clear(); resetRadicalToggles(); resetAllowedToggles(); }), icon: const Icon(Icons.restore), color: AppTheme.jishoGreen.background, iconSize: fontSize * 1.3, ), ...radicals .map( (key, value) => MapEntry( key, value .where((r) => allowedToggles[r]!) .map((r) => radicalGridElement(r)) .toList() ..insert( 0, radicalGridElement(key.toString(), isNumber: true), ), ), ) .values .where((element) => element.length != 1) .expand((l) => l) .toList() ]; Widget kanjiGridElement(String kanji) { const color = LightTheme.defaultMenuGreyNormal; return InkWell( onTap: () => Navigator.popAndPushNamed( context, Routes.kanjiSearch, arguments: kanji, ), child: Container( decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(5)), color: color.background, ), alignment: Alignment.center, child: Text( kanji, style: TextStyle( color: color.foreground, fontSize: fontSize, ), ), ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Choose by radicals')), body: Column( children: [ Expanded( child: (suggestions.isEmpty) ? Center( child: BlocBuilder( builder: (context, state) => Text( 'Toggle a radical to start', style: TextStyle( fontSize: fontSize * 0.8, color: state.theme.menuGreyNormal.background, ), ), ), ) : GridView.count( crossAxisCount: 6, mainAxisSpacing: 10, crossAxisSpacing: 10, padding: const EdgeInsets.all(10), children: suggestions.map((s) => kanjiGridElement(s)).toList(), ), ), Divider( color: AppTheme.jishoGreen.background, thickness: 3, height: 30, indent: 5, endIndent: 5, ), Expanded( child: GridView.count( crossAxisCount: 6, mainAxisSpacing: 10, crossAxisSpacing: 10, padding: const EdgeInsets.all(10), children: radicalGridElements, ), ), ], ), ); } }