1
1
import 'package:cached_network_image/cached_network_image.dart' ;
2
2
import 'package:flutter/material.dart' ;
3
- import 'package:flutter_link_previewer/flutter_link_previewer .dart' ;
3
+ import 'package:link_preview_generator/link_preview_generator .dart' ;
4
4
5
5
import 'package:flutter_twitter_clone/helper/utility.dart' ;
6
6
import 'package:flutter_twitter_clone/state/feedState.dart' ;
7
7
import 'package:flutter_twitter_clone/ui/theme/theme.dart' ;
8
+ import 'package:link_preview_generator/src/utils/analyzer.dart'
9
+ show
10
+ LinkPreviewAnalyzer; //FIXME Don't import implementation files from another package.
8
11
import 'package:provider/provider.dart' ;
9
12
10
13
class LinkPreviewer extends StatelessWidget {
@@ -31,42 +34,132 @@ class LinkPreviewer extends StatelessWidget {
31
34
@override
32
35
Widget build (BuildContext context) {
33
36
var state = Provider .of <FeedState >(context, listen: false );
37
+
34
38
var uri = url ?? getUrl ();
35
39
if (uri == null ) {
36
40
return const SizedBox .shrink ();
37
41
} else if (uri.contains ("page.link/" )) {
38
42
/// `flutter_link_preview` package is unable to fetch firebase dynamic link meta data
39
43
return const SizedBox .shrink ();
40
44
}
41
- final style = TextStyle (
42
- fontSize: 0 ,
43
- );
44
-
45
- try {
46
- return LinkPreview (
47
- enableAnimation: false ,
48
- onPreviewDataFetched: (data) {
49
- state.addPreviewData (uri, data);
50
- },
51
- imageBuilder: (image) {
52
- return ClipRRect (
53
- borderRadius: BorderRadius .circular (10.0 ),
54
- child: CachedNetworkImage (
55
- imageUrl: image,
45
+ if (state.linkWebInfos.containsKey (uri))
46
+ return _buildLinkPreview (
47
+ state.linkWebInfos[uri]! , uri, Theme .of (context));
48
+ return FutureBuilder (
49
+ builder: (BuildContext context, AsyncSnapshot <InfoBase ?> snapshot) {
50
+ if (snapshot.hasData) {
51
+ state.addWebInfo (uri, snapshot.data! as WebInfo );
52
+ return _buildLinkPreview (
53
+ snapshot.data! as WebInfo , uri, Theme .of (context));
54
+ }
55
+ if (snapshot.hasError) {
56
+ return SizedBox .shrink ();
57
+ }
58
+ return Container (
59
+ width: MediaQuery .of (context).size.width,
60
+ decoration: BoxDecoration (
61
+ borderRadius: BorderRadius .circular (8 ),
62
+ color: const Color .fromRGBO (248 , 248 , 248 , 1.0 ),
56
63
),
57
- ). ripple (() {
58
- Utility . launchURL (uri);
59
- } );
64
+ alignment : Alignment .center,
65
+ child : const Text ( 'Fetching data...' ),
66
+ );
60
67
},
61
- linkStyle: style,
62
- previewData:
63
- state.linkDataPreviews[uri], // Pass the preview data from the state
64
- text: uri,
65
- width: MediaQuery .of (context).size.width,
68
+ future: LinkPreviewAnalyzer .getInfo (uri,
69
+ cacheDuration: Duration (hours: 24 ), multimedia: true ));
70
+ }
71
+
72
+ Widget _buildLinkPreview (WebInfo info, String uri, ThemeData theme) {
73
+ var image = LinkPreviewAnalyzer .isNotEmpty (info.image)
74
+ ? info.image
75
+ : LinkPreviewAnalyzer .isNotEmpty (info.icon)
76
+ ? info.icon
77
+ : "" ; //TODO Placeholder/error image
78
+ if (! LinkPreviewAnalyzer .isNotEmpty (info.title) &&
79
+ LinkPreviewAnalyzer .isNotEmpty (info.image)) {
80
+ return CachedNetworkImage (
81
+ imageUrl: image,
82
+ fit: BoxFit .contain,
83
+ ).ripple (
84
+ () {
85
+ Utility .launchURL (uri);
86
+ },
87
+ borderRadius: BorderRadius .circular (10 ),
66
88
);
67
- } catch (e) {
68
- cprint (e);
69
- return SizedBox .shrink ();
70
89
}
90
+ if (! LinkPreviewAnalyzer .isNotEmpty (info.title)) return const SizedBox ();
91
+
92
+ return Padding (
93
+ padding: const EdgeInsets .only (top: 8 ),
94
+ child: Container (
95
+ decoration: BoxDecoration (
96
+ borderRadius: BorderRadius .circular (10 ),
97
+ border: Border .all (color: AppColor .extraLightGrey),
98
+ color: theme.colorScheme.onPrimary),
99
+ child: Column (
100
+ crossAxisAlignment: CrossAxisAlignment .start,
101
+ mainAxisAlignment: MainAxisAlignment .center,
102
+ children: < Widget > [
103
+ if (LinkPreviewAnalyzer .isNotEmpty (image))
104
+ ClipRRect (
105
+ borderRadius: BorderRadius .vertical (top: Radius .circular (10 )),
106
+ child: Container (
107
+ height: 140 ,
108
+ width: double .infinity,
109
+ decoration: BoxDecoration (
110
+ border: Border (
111
+ bottom: BorderSide (color: theme.dividerColor, width: 1 ),
112
+ ),
113
+ ),
114
+ child: CachedNetworkImage (
115
+ imageUrl: image,
116
+ fit: BoxFit .cover,
117
+ ),
118
+ ),
119
+ ),
120
+ const SizedBox (height: 4 ),
121
+ if (LinkPreviewAnalyzer .isNotEmpty (info.title))
122
+ Padding (
123
+ padding: const EdgeInsets .symmetric (horizontal: 8 , vertical: 4 ),
124
+ child: Text (
125
+ info.title.trim (),
126
+ maxLines: 1 ,
127
+ overflow: TextOverflow .ellipsis,
128
+ style: TextStyles .titleStyle.copyWith (
129
+ fontSize: 14 ,
130
+ fontWeight: FontWeight .bold,
131
+ color: Colors .black87),
132
+ ),
133
+ ),
134
+ if (LinkPreviewAnalyzer .isNotEmpty (info.description))
135
+ Padding (
136
+ padding: const EdgeInsets .symmetric (horizontal: 8 , vertical: 4 ),
137
+ child: Text (
138
+ info.description.trim (),
139
+ maxLines: 2 ,
140
+ overflow: TextOverflow .ellipsis,
141
+ style: TextStyles .subtitleStyle.copyWith (fontSize: 12 ),
142
+ ),
143
+ ),
144
+ Padding (
145
+ padding: EdgeInsets .only (bottom: 5 , left: 8 , right: 8 ),
146
+ child: Text (
147
+ Uri .tryParse (uri)! .authority,
148
+ maxLines: 1 ,
149
+ overflow: TextOverflow .ellipsis,
150
+ style: TextStyles .subtitleStyle.copyWith (
151
+ fontWeight: FontWeight .w400,
152
+ fontSize: info.title.isNotEmpty ? 14 : 16 ),
153
+ ),
154
+ ),
155
+ ],
156
+ ),
157
+ ).ripple (
158
+ () {
159
+ Utility .launchURL (uri);
160
+ },
161
+ borderRadius: BorderRadius .circular (8 ),
162
+ ),
163
+ );
71
164
}
72
165
}
0 commit comments