import 'dart:io'; import 'dart:convert'; import 'dart:async'; import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:color_thief_flutter/color_thief_flutter.dart'; import 'package:dio/dio.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'package:fluttertoast/fluttertoast.dart'; import '../type/searchpodcast.dart'; import '../state/subscribe_podcast.dart'; import '../util/context_extension.dart'; import '../webfeed/webfeed.dart'; import '../.env.dart'; class MyHomePageDelegate extends SearchDelegate { final String searchFieldLabel; MyHomePageDelegate({this.searchFieldLabel}) : super(searchFieldLabel: searchFieldLabel); static Future getList(String searchText) async { String apiKey = environment['apiKey']; String url = "https://listennotes.p.rapidapi.com/api/v1/search?only_in=title%2Cdescription&q=" + searchText + "&sort_by_date=0&type=podcast"; Response response = await Dio().get(url, options: Options(headers: { 'X-Mashape-Key': "$apiKey", 'Accept': "application/json" })); Map searchResultMap = jsonDecode(response.toString()); var searchResult = SearchPodcast.fromJson(searchResultMap); return searchResult.results; } static Future getRss(String url) async { try { BaseOptions options = new BaseOptions( connectTimeout: 10000, receiveTimeout: 10000, ); Response response = await Dio(options).get(url); var p = RssFeed.parse(response.data); return OnlinePodcast( rss: url, title: p.title, publisher: p.author, description: p.description, image: p.itunes.image.href); } catch (e) { throw e; } } RegExp rssExp = RegExp(r'^(https?):\/\/(.*)'); Widget invalidRss() => Container( height: 50, alignment: Alignment.center, child: Text('Invalid rss link'), ); @override ThemeData appBarTheme(BuildContext context) => Theme.of(context); @override Widget buildLeading(BuildContext context) { return IconButton( tooltip: 'Back', icon: AnimatedIcon( icon: AnimatedIcons.menu_arrow, progress: transitionAnimation, ), onPressed: () { close(context, 1); }, ); } @override Widget buildSuggestions(BuildContext context) { if (query.isEmpty) return Center( child: Container( padding: EdgeInsets.only(top: 400), child: Image( image: Theme.of(context).brightness == Brightness.light ? AssetImage('assets/listennotes.png') : AssetImage('assets/listennotes_light.png'), height: 20, ), )); else if (rssExp.stringMatch(query) != null) return FutureBuilder( future: getRss(rssExp.stringMatch(query)), builder: (context, snapshot) { if (snapshot.hasError) return invalidRss(); else if (snapshot.hasData) return SearchResult( onlinePodcast: snapshot.data, ); else return Container( padding: EdgeInsets.only(top: 200), alignment: Alignment.topCenter, child: CircularProgressIndicator(), ); }, ); else return FutureBuilder( future: getList(query), builder: (BuildContext context, AsyncSnapshot snapshot) { if (!snapshot.hasData && query != null) return Container( padding: EdgeInsets.only(top: 200), alignment: Alignment.topCenter, child: CircularProgressIndicator(), ); List content = snapshot.data; return ListView.builder( scrollDirection: Axis.vertical, itemCount: content.length, itemBuilder: (BuildContext context, int index) { return SearchResult( onlinePodcast: content[index], ); }, ); }, ); } @override List buildActions(BuildContext context) { return [ if (query.isEmpty) Center() else IconButton( tooltip: 'Clear', icon: const Icon(Icons.clear), onPressed: () { query = ''; }, ), ]; } @override Widget buildResults(BuildContext context) { if (query.isEmpty) return Container( height: 10, width: 10, margin: EdgeInsets.only(top: 400), child: SizedBox( height: 10, child: Image.asset( 'assets/listennote.png', fit: BoxFit.fill, ), ), ); else if (rssExp.stringMatch(query) != null) return FutureBuilder( future: getRss(rssExp.stringMatch(query)), builder: (context, snapshot) { if (snapshot.hasError) return invalidRss(); else if (snapshot.hasData) return SearchResult( onlinePodcast: snapshot.data, ); else return Container( padding: EdgeInsets.only(top: 200), alignment: Alignment.topCenter, child: CircularProgressIndicator(), ); }, ); else return FutureBuilder( future: getList(query), builder: (BuildContext context, AsyncSnapshot snapshot) { if (!snapshot.hasData && query != null) return Container( padding: EdgeInsets.only(top: 200), alignment: Alignment.topCenter, child: CircularProgressIndicator(), ); List content = snapshot.data; return ListView.builder( scrollDirection: Axis.vertical, itemCount: content.length, itemBuilder: (BuildContext context, int index) { return SearchResult( onlinePodcast: content[index], ); }, ); }, ); } } class SearchResult extends StatefulWidget { final OnlinePodcast onlinePodcast; SearchResult({this.onlinePodcast, Key key}) : super(key: key); @override _SearchResultState createState() => _SearchResultState(); } class _SearchResultState extends State with SingleTickerProviderStateMixin { bool _issubscribe; bool _showDes; AnimationController _controller; Animation _animation; double _value; @override void initState() { super.initState(); _issubscribe = false; _showDes = false; _value = 0; _controller = AnimationController(vsync: this, duration: Duration(milliseconds: 300)); _animation = Tween(begin: 0.0, end: 1.0).animate(_controller) ..addListener(() { setState(() { _value = _animation.value; }); }); } @override void dispose() { _controller.dispose(); super.dispose(); } Future getColor(File file) async { final imageProvider = FileImage(file); var colorImage = await getImageFromProvider(imageProvider); var color = await getColorFromImage(colorImage); String primaryColor = color.toString(); return primaryColor; } @override Widget build(BuildContext context) { var subscribeWorker = Provider.of(context, listen: false); savePodcast(OnlinePodcast podcast) { SubscribeItem item = SubscribeItem(podcast.rss, podcast.title, imgUrl: podcast.image); subscribeWorker.setSubscribeItem(item); } return Container( decoration: BoxDecoration( border: Border( bottom: Divider.createBorderSide(context), ), ), child: Column( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ ListTile( contentPadding: EdgeInsets.symmetric(horizontal: 20.0), onTap: () { setState(() { _showDes = !_showDes; if (_value == 0) _controller.forward(); else _controller.reverse(); }); }, leading: ClipRRect( borderRadius: BorderRadius.all(Radius.circular(20.0)), child: Image.network( widget.onlinePodcast.image, height: 40.0, width: 40.0, fit: BoxFit.fitWidth, alignment: Alignment.center, ), ), title: Text(widget.onlinePodcast.title), subtitle: Text(widget.onlinePodcast.publisher ?? ''), trailing: Row( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Transform.rotate( angle: math.pi * _value, child: Icon(Icons.keyboard_arrow_down), ), Padding(padding: EdgeInsets.only(right: 10.0)), Container( width: 100, height: 35, child: !_issubscribe ? OutlineButton( highlightedBorderColor: context.accentColor, splashColor: context.accentColor.withOpacity(0.8), child: Text('Subscribe', style: TextStyle( color: Theme.of(context).accentColor)), onPressed: () { savePodcast(widget.onlinePodcast); setState(() => _issubscribe = true); Fluttertoast.showToast( msg: 'Podcast subscribed', gravity: ToastGravity.BOTTOM, ); }) : OutlineButton( color: context.accentColor.withOpacity(0.8), highlightedBorderColor: Colors.grey[500], disabledTextColor: Colors.grey[500], child: Text('Subscribe'), disabledBorderColor: Colors.grey[500], onPressed: () {}), ), ], ), ), _showDes ? Container( alignment: Alignment.centerLeft, decoration: BoxDecoration( color: Theme.of(context).accentColor, borderRadius: BorderRadius.only( topRight: Radius.circular(15.0), bottomLeft: Radius.circular(15.0), bottomRight: Radius.circular(15.0), )), margin: EdgeInsets.only(left: 70, right: 50, bottom: 10.0), padding: EdgeInsets.all(15.0), child: Text( widget.onlinePodcast.description.trim(), maxLines: 3, overflow: TextOverflow.ellipsis, style: Theme.of(context) .textTheme .bodyText1 .copyWith(color: Colors.white), ), ) : Center(), ], ), ); } }