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,130 @@ 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 <WebInfo > snapshot) {
50
+ if (snapshot.hasData) {
51
+ state.addWebInfo (uri, snapshot.data! );
52
+ return _buildLinkPreview (snapshot.data! , uri, Theme .of (context));
53
+ }
54
+ if (snapshot.hasError) {
55
+ return SizedBox .shrink ();
56
+ }
57
+ return Container (
58
+ width: MediaQuery .of (context).size.width,
59
+ decoration: BoxDecoration (
60
+ borderRadius: BorderRadius .circular (8 ),
61
+ color: const Color .fromRGBO (248 , 248 , 248 , 1.0 ),
56
62
),
57
- ). ripple (() {
58
- Utility . launchURL (uri);
59
- } );
63
+ alignment : Alignment .center,
64
+ child : const Text ( 'Fetching data...' ),
65
+ );
60
66
},
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,
67
+ future: LinkPreview .scrapeFromURL (uri));
68
+ }
69
+
70
+ Widget _buildLinkPreview (WebInfo info, String uri, ThemeData theme) {
71
+ var image = LinkPreviewAnalyzer .isNotEmpty (info.image)
72
+ ? info.image
73
+ : LinkPreviewAnalyzer .isNotEmpty (info.icon)
74
+ ? info.icon
75
+ : "" ; //TODO Placeholder/error image
76
+ if (! LinkPreviewAnalyzer .isNotEmpty (info.title) &&
77
+ LinkPreviewAnalyzer .isNotEmpty (info.image)) {
78
+ return CachedNetworkImage (
79
+ imageUrl: image,
80
+ fit: BoxFit .contain,
81
+ ).ripple (
82
+ () {
83
+ Utility .launchURL (uri);
84
+ },
85
+ borderRadius: BorderRadius .circular (10 ),
66
86
);
67
- } catch (e) {
68
- cprint (e);
69
- return SizedBox .shrink ();
70
87
}
88
+ if (! LinkPreviewAnalyzer .isNotEmpty (info.title)) return const SizedBox ();
89
+
90
+ return Padding (
91
+ padding: const EdgeInsets .only (top: 8 ),
92
+ child: Container (
93
+ decoration: BoxDecoration (
94
+ borderRadius: BorderRadius .circular (10 ),
95
+ border: Border .all (color: AppColor .extraLightGrey),
96
+ color: theme.colorScheme.onPrimary),
97
+ child: Column (
98
+ crossAxisAlignment: CrossAxisAlignment .start,
99
+ mainAxisAlignment: MainAxisAlignment .center,
100
+ children: < Widget > [
101
+ if (LinkPreviewAnalyzer .isNotEmpty (image))
102
+ ClipRRect (
103
+ borderRadius: BorderRadius .vertical (top: Radius .circular (10 )),
104
+ child: Container (
105
+ height: 140 ,
106
+ width: double .infinity,
107
+ decoration: BoxDecoration (
108
+ border: Border (
109
+ bottom: BorderSide (color: theme.dividerColor, width: 1 ),
110
+ ),
111
+ ),
112
+ child: CachedNetworkImage (
113
+ imageUrl: image,
114
+ fit: BoxFit .cover,
115
+ ),
116
+ ),
117
+ ),
118
+ const SizedBox (height: 4 ),
119
+ if (LinkPreviewAnalyzer .isNotEmpty (info.title))
120
+ Padding (
121
+ padding: const EdgeInsets .symmetric (horizontal: 8 , vertical: 4 ),
122
+ child: Text (
123
+ info.title.trim (),
124
+ maxLines: 1 ,
125
+ overflow: TextOverflow .ellipsis,
126
+ style: TextStyles .titleStyle.copyWith (
127
+ fontSize: 14 ,
128
+ fontWeight: FontWeight .bold,
129
+ color: Colors .black87),
130
+ ),
131
+ ),
132
+ if (LinkPreviewAnalyzer .isNotEmpty (info.description))
133
+ Padding (
134
+ padding: const EdgeInsets .symmetric (horizontal: 8 , vertical: 4 ),
135
+ child: Text (
136
+ info.description.trim (),
137
+ maxLines: 2 ,
138
+ overflow: TextOverflow .ellipsis,
139
+ style: TextStyles .subtitleStyle.copyWith (fontSize: 12 ),
140
+ ),
141
+ ),
142
+ Padding (
143
+ padding: EdgeInsets .only (bottom: 5 , left: 8 , right: 8 ),
144
+ child: Text (
145
+ Uri .tryParse (uri)! .authority,
146
+ maxLines: 1 ,
147
+ overflow: TextOverflow .ellipsis,
148
+ style: TextStyles .subtitleStyle.copyWith (
149
+ fontWeight: FontWeight .w400,
150
+ fontSize: info.title.isNotEmpty ? 14 : 16 ),
151
+ ),
152
+ ),
153
+ ],
154
+ ),
155
+ ).ripple (
156
+ () {
157
+ Utility .launchURL (uri);
158
+ },
159
+ borderRadius: BorderRadius .circular (8 ),
160
+ ),
161
+ );
71
162
}
72
163
}
0 commit comments