mirror of
https://github.com/Livinglist/Hacki.git
synced 2025-08-06 18:24:42 +08:00
Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
2af10391bc |
@ -1,10 +1,12 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hacki/config/constants.dart';
|
||||
import 'package:hacki/models/models.dart';
|
||||
import 'package:hacki/styles/styles.dart';
|
||||
import 'package:memoize/memoize.dart';
|
||||
|
||||
class LinkView extends StatelessWidget {
|
||||
const LinkView({
|
||||
LinkView({
|
||||
super.key,
|
||||
required this.metadata,
|
||||
required this.url,
|
||||
@ -13,18 +15,19 @@ class LinkView extends StatelessWidget {
|
||||
required this.description,
|
||||
required this.onTap,
|
||||
required this.showMetadata,
|
||||
required this.showUrl,
|
||||
required bool showUrl,
|
||||
required this.bodyMaxLines,
|
||||
this.imageUri,
|
||||
this.imagePath,
|
||||
this.titleTextStyle,
|
||||
this.bodyTextStyle,
|
||||
this.showMultiMedia = true,
|
||||
this.bodyTextOverflow,
|
||||
this.bodyMaxLines,
|
||||
this.isIcon = false,
|
||||
this.bgColor,
|
||||
this.radius = 0,
|
||||
}) : assert(
|
||||
}) : showUrl = showUrl && url.isNotEmpty,
|
||||
assert(
|
||||
!showMultiMedia ||
|
||||
(showMultiMedia && (imageUri != null || imagePath != null)),
|
||||
'imageUri or imagePath cannot be null when showMultiMedia is true',
|
||||
@ -42,14 +45,17 @@ class LinkView extends StatelessWidget {
|
||||
final TextStyle? bodyTextStyle;
|
||||
final bool showMultiMedia;
|
||||
final TextOverflow? bodyTextOverflow;
|
||||
final int? bodyMaxLines;
|
||||
final int bodyMaxLines;
|
||||
final bool isIcon;
|
||||
final double radius;
|
||||
final Color? bgColor;
|
||||
final bool showMetadata;
|
||||
final bool showUrl;
|
||||
|
||||
double computeTitleFontSize(double width) {
|
||||
static final double Function(double) _getTitleFontSize =
|
||||
memo1(_computeTitleFontSize);
|
||||
|
||||
static double _computeTitleFontSize(double width) {
|
||||
double size = width * 0.13;
|
||||
if (size > 15) {
|
||||
size = 15;
|
||||
@ -57,16 +63,26 @@ class LinkView extends StatelessWidget {
|
||||
return size;
|
||||
}
|
||||
|
||||
int computeTitleLines(double layoutHeight) {
|
||||
static final int Function(double) _getTitleLines = memo1(_computeTitleLines);
|
||||
|
||||
static int _computeTitleLines(double layoutHeight) {
|
||||
return layoutHeight >= 100 ? 2 : 1;
|
||||
}
|
||||
|
||||
int computeBodyLines(double layoutHeight) {
|
||||
int lines = 1;
|
||||
if (layoutHeight > 40) {
|
||||
lines += (layoutHeight - 40.0) ~/ 15.0;
|
||||
}
|
||||
return lines;
|
||||
static final int Function(int, bool, bool, String?) _getBodyLines =
|
||||
memo4(_computeBodyLines);
|
||||
|
||||
static int _computeBodyLines(
|
||||
int bodyMaxLines,
|
||||
bool showMetadata,
|
||||
bool showUrl,
|
||||
String? fontFamily,
|
||||
) {
|
||||
final int maxLines = bodyMaxLines -
|
||||
(showMetadata ? 1 : 0) -
|
||||
(showUrl ? 1 : 0) +
|
||||
(fontFamily == Font.ubuntuMono.name ? 1 : 0);
|
||||
return maxLines;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -76,15 +92,15 @@ class LinkView extends StatelessWidget {
|
||||
final double layoutWidth = constraints.biggest.width;
|
||||
final double layoutHeight = constraints.biggest.height;
|
||||
|
||||
final TextStyle titleFontSize = titleTextStyle ??
|
||||
final TextStyle titleFontStyle = titleTextStyle ??
|
||||
TextStyle(
|
||||
fontSize: computeTitleFontSize(layoutWidth),
|
||||
fontSize: _getTitleFontSize(layoutWidth),
|
||||
color: Palette.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
);
|
||||
final TextStyle bodyFontSize = bodyTextStyle ??
|
||||
final TextStyle bodyFontStyle = bodyTextStyle ??
|
||||
TextStyle(
|
||||
fontSize: computeTitleFontSize(layoutWidth) - 1,
|
||||
fontSize: _getTitleFontSize(layoutWidth) - 1,
|
||||
color: Palette.grey,
|
||||
fontWeight: FontWeight.w400,
|
||||
);
|
||||
@ -96,7 +112,7 @@ class LinkView extends StatelessWidget {
|
||||
if (showMultiMedia)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 5,
|
||||
right: 8,
|
||||
top: 5,
|
||||
bottom: 5,
|
||||
),
|
||||
@ -112,7 +128,7 @@ class LinkView extends StatelessWidget {
|
||||
imageUrl: imageUri!,
|
||||
fit: isIcon ? BoxFit.scaleDown : BoxFit.fitWidth,
|
||||
memCacheHeight: layoutHeight.toInt() * 4,
|
||||
errorWidget: (BuildContext context, _, dynamic __) {
|
||||
errorWidget: (BuildContext context, _, __) {
|
||||
return Image.asset(
|
||||
Constants.hackerNewsLogoPath,
|
||||
fit: BoxFit.cover,
|
||||
@ -124,22 +140,85 @@ class LinkView extends StatelessWidget {
|
||||
else
|
||||
const SizedBox(width: 5),
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 3),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
_buildTitleContainer(
|
||||
titleFontSize,
|
||||
computeTitleLines(layoutHeight),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.fontFamily ==
|
||||
Font.robotoSlab.name
|
||||
? 2
|
||||
: 4,
|
||||
),
|
||||
_buildBodyContainer(
|
||||
bodyFontSize,
|
||||
computeBodyLines(layoutHeight),
|
||||
)
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
alignment: Alignment.topLeft,
|
||||
child: Text(
|
||||
title,
|
||||
style: titleFontStyle,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: _getTitleLines(layoutHeight),
|
||||
),
|
||||
),
|
||||
if (showUrl && url.isNotEmpty)
|
||||
Container(
|
||||
alignment: Alignment.topLeft,
|
||||
child: Text(
|
||||
'($readableUrl)',
|
||||
textAlign: TextAlign.left,
|
||||
style: titleFontStyle.copyWith(
|
||||
color: Palette.grey,
|
||||
fontSize: titleFontStyle.fontSize == null
|
||||
? 12
|
||||
: titleFontStyle.fontSize! - 4,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
overflow:
|
||||
bodyTextOverflow ?? TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (showMetadata)
|
||||
Container(
|
||||
alignment: Alignment.topLeft,
|
||||
margin: const EdgeInsets.only(top: 2),
|
||||
child: Text(
|
||||
metadata,
|
||||
textAlign: TextAlign.left,
|
||||
style: bodyFontStyle.copyWith(
|
||||
fontSize: bodyFontStyle.fontSize == null
|
||||
? 12
|
||||
: bodyFontStyle.fontSize! - 2,
|
||||
),
|
||||
overflow: bodyTextOverflow ?? TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
alignment: Alignment.topLeft,
|
||||
child: Text(
|
||||
description,
|
||||
textAlign: TextAlign.left,
|
||||
style: bodyFontStyle,
|
||||
overflow: bodyTextOverflow ?? TextOverflow.ellipsis,
|
||||
maxLines: _getBodyLines(
|
||||
bodyMaxLines,
|
||||
showMetadata,
|
||||
showUrl,
|
||||
Theme.of(context).textTheme.bodyMedium?.fontFamily,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -148,81 +227,4 @@ class LinkView extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTitleContainer(TextStyle titleTS, int maxLines) {
|
||||
final bool showUrl = this.showUrl && url.isNotEmpty;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(4, 2, 3, 0),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
alignment: Alignment.topLeft,
|
||||
child: Text(
|
||||
title,
|
||||
style: titleTS,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: maxLines,
|
||||
),
|
||||
),
|
||||
if (showUrl)
|
||||
Container(
|
||||
alignment: Alignment.topLeft,
|
||||
child: Text(
|
||||
'($readableUrl)',
|
||||
textAlign: TextAlign.left,
|
||||
style: titleTS.copyWith(
|
||||
color: Palette.grey,
|
||||
fontSize:
|
||||
titleTS.fontSize == null ? 12 : titleTS.fontSize! - 4,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
overflow: bodyTextOverflow ?? TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBodyContainer(TextStyle bodyTS, int maxLines) {
|
||||
return Expanded(
|
||||
flex: 2,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(5, 2, 5, 0),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
if (showMetadata)
|
||||
Container(
|
||||
alignment: Alignment.topLeft,
|
||||
child: Text(
|
||||
metadata,
|
||||
textAlign: TextAlign.left,
|
||||
style: bodyTS.copyWith(
|
||||
fontSize:
|
||||
bodyTS.fontSize == null ? 12 : bodyTS.fontSize! - 2,
|
||||
),
|
||||
overflow: bodyTextOverflow ?? TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
alignment: Alignment.topLeft,
|
||||
child: Text(
|
||||
description,
|
||||
textAlign: TextAlign.left,
|
||||
style: bodyTS,
|
||||
overflow: bodyTextOverflow ?? TextOverflow.ellipsis,
|
||||
maxLines: (bodyMaxLines ?? maxLines) -
|
||||
(showMetadata ? 1 : 0) -
|
||||
(showUrl && url.isNotEmpty ? 1 : 0),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -600,6 +600,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
memoize:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: memoize
|
||||
sha256: "51481d328c86cbdc59711369179bac88551ca0556569249be5317e66fc796cac"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1,6 +1,6 @@
|
||||
name: hacki
|
||||
description: A Hacker News reader.
|
||||
version: 1.2.4+97
|
||||
version: 1.2.5+98
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
@ -45,6 +45,7 @@ dependencies:
|
||||
intl: ^0.18.0
|
||||
linkify: ^4.1.0
|
||||
logger: ^1.1.0
|
||||
memoize: ^3.0.0
|
||||
package_info_plus: ^3.0.3
|
||||
path: ^1.8.2
|
||||
path_provider: ^2.0.12
|
||||
|
Reference in New Issue
Block a user