diff --git a/lib/pages/article/view.dart b/lib/pages/article/view.dart index 63a92e8d..e843b9bb 100644 --- a/lib/pages/article/view.dart +++ b/lib/pages/article/view.dart @@ -268,29 +268,25 @@ class _ArticlePageState extends State return LayoutBuilder(builder: (context, constraints) { final maxWidth = constraints.maxWidth - 2 * padding - 24; return Padding( - padding: EdgeInsets.symmetric(horizontal: padding), - child: SelectionArea( - child: CustomScrollView( - controller: _articleCtr.scrollController, - physics: const AlwaysScrollableScrollPhysics(), - slivers: [ - _buildContent(theme, maxWidth), - SelectionContainer.disabled( - child: SliverToBoxAdapter( - child: Divider( - thickness: 8, - color: theme.dividerColor - .withValues(alpha: 0.05), - ), - )), - SelectionContainer.disabled( - child: _buildReplyHeader(theme)), - SelectionContainer.disabled( - child: Obx(() => _buildReplyList(theme, - _articleCtr.loadingState.value))), - ], + padding: EdgeInsets.symmetric(horizontal: padding), + child: CustomScrollView( + controller: _articleCtr.scrollController, + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + _buildContent(theme, maxWidth), + SliverToBoxAdapter( + child: Divider( + thickness: 8, + color: + theme.dividerColor.withValues(alpha: 0.05), + ), ), - )); + _buildReplyHeader(theme), + Obx(() => _buildReplyList( + theme, _articleCtr.loadingState.value)), + ], + ), + ); }); } else { return Row( @@ -302,8 +298,7 @@ class _ArticlePageState extends State builder: (context, constraints) { final maxWidth = constraints.maxWidth - padding / 4 - 24; - return SelectionArea( - child: CustomScrollView( + return CustomScrollView( controller: _articleCtr.scrollController, physics: const AlwaysScrollableScrollPhysics(), slivers: [ @@ -317,7 +312,7 @@ class _ArticlePageState extends State sliver: _buildContent(theme, maxWidth), ), ], - )); + ); }, ), ), @@ -426,89 +421,90 @@ class _ArticlePageState extends State ?.pics?.isNotEmpty == true) SliverToBoxAdapter( - child: SelectionContainer.disabled(child: Builder( - builder: (context) { - final pics = _articleCtr - .opusData!.modules.moduleTop!.display!.album!.pics!; - final length = pics.length; - final first = pics.first; - double height; - double paddingRight; - if (first.height != null && first.width != null) { - final ratio = first.height! / first.width!; - height = min(maxWidth * ratio, Get.height * 0.55); - paddingRight = (maxWidth - height / ratio) / 2 + 12; - } else { - height = Get.height * 0.55; - paddingRight = 12; - } - return Stack( - clipBehavior: Clip.none, - children: [ - Container( - height: height, - width: maxWidth, - margin: const EdgeInsets.only(bottom: 10), - child: PageView.builder( - physics: const ClampingScrollPhysics(), - onPageChanged: (value) { - _articleCtr.topIndex.value = value; - }, - itemCount: length, - itemBuilder: (context, index) { - final pic = pics[index]; - return GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () => context.imageView( - imgList: pics - .map((e) => SourceModel(url: e.url!)) - .toList(), - initialPage: index, - ), - child: Hero( - tag: pic.url!, - child: Stack( - clipBehavior: Clip.none, - alignment: Alignment.center, - children: [ - Positioned.fill( - child: CachedNetworkImage( - fit: pic.isLongPic == true - ? BoxFit.cover - : null, - imageUrl: Utils.thumbnailImgUrl( - pic.url, 60), - ), - ), - if (pic.isLongPic == true) - PBadge( - text: '长图', - type: PBadgeType.primary, - right: paddingRight, - bottom: 12, - ), - ], + child: Builder( + builder: (context) { + final pics = _articleCtr.opusData!.modules.moduleTop! + .display!.album!.pics!; + final length = pics.length; + final first = pics.first; + double height; + double paddingRight; + if (first.height != null && first.width != null) { + final ratio = first.height! / first.width!; + height = min(maxWidth * ratio, Get.height * 0.55); + paddingRight = (maxWidth - height / ratio) / 2 + 12; + } else { + height = Get.height * 0.55; + paddingRight = 12; + } + return Stack( + clipBehavior: Clip.none, + children: [ + Container( + height: height, + width: maxWidth, + margin: const EdgeInsets.only(bottom: 10), + child: PageView.builder( + physics: const ClampingScrollPhysics(), + onPageChanged: (value) { + _articleCtr.topIndex.value = value; + }, + itemCount: length, + itemBuilder: (context, index) { + final pic = pics[index]; + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => context.imageView( + imgList: pics + .map( + (e) => SourceModel(url: e.url!)) + .toList(), + initialPage: index, ), - ), - ); - }, + child: Hero( + tag: pic.url!, + child: Stack( + clipBehavior: Clip.none, + alignment: Alignment.center, + children: [ + Positioned.fill( + child: CachedNetworkImage( + fit: pic.isLongPic == true + ? BoxFit.cover + : null, + imageUrl: Utils.thumbnailImgUrl( + pic.url, 60), + ), + ), + if (pic.isLongPic == true) + PBadge( + text: '长图', + type: PBadgeType.primary, + right: paddingRight, + bottom: 12, + ), + ], + ), + ), + ); + }, + ), ), - ), - Obx( - () => PBadge( - top: 12, - right: paddingRight, - type: PBadgeType.gray, - text: - '${_articleCtr.topIndex.value + 1}/$length'), - ), - ], - ); - }, - ))), + Obx( + () => PBadge( + top: 12, + right: paddingRight, + type: PBadgeType.gray, + text: + '${_articleCtr.topIndex.value + 1}/$length'), + ), + ], + ); + }, + ), + ), if (_articleCtr.summary.title != null) - SelectionContainer.disabled( - child: SliverToBoxAdapter( + SliverToBoxAdapter( child: Text( _articleCtr.summary.title!, style: const TextStyle( @@ -516,9 +512,8 @@ class _ArticlePageState extends State fontWeight: FontWeight.bold, ), ), - )), - SelectionContainer.disabled( - child: SliverToBoxAdapter( + ), + SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.symmetric(vertical: 10), child: GestureDetector( @@ -559,16 +554,15 @@ class _ArticlePageState extends State ), ), ), - )), + ), if (_articleCtr.type != 'read' && _articleCtr.opusData?.modules.moduleCollection != null) - SelectionContainer.disabled( - child: SliverToBoxAdapter( + SliverToBoxAdapter( child: opusCollection( theme, _articleCtr.opusData!.modules.moduleCollection!, ), - )), + ), content, ], ); diff --git a/lib/pages/article/widgets/html_render.dart b/lib/pages/article/widgets/html_render.dart index 7ad1bb7b..be9a9020 100644 --- a/lib/pages/article/widgets/html_render.dart +++ b/lib/pages/article/widgets/html_render.dart @@ -126,15 +126,17 @@ Widget htmlRender({ margin: Margins.zero, ), }; - return element != null - ? Html.fromElement( - documentElement: element, - extensions: extensions, - style: style, - ) - : Html( - data: html, - extensions: extensions, - style: style, - ); + return SelectionArea( + child: element != null + ? Html.fromElement( + documentElement: element, + extensions: extensions, + style: style, + ) + : Html( + data: html, + extensions: extensions, + style: style, + ), + ); } diff --git a/lib/pages/article/widgets/opus_content.dart b/lib/pages/article/widgets/opus_content.dart index eb1a72f7..103c4274 100644 --- a/lib/pages/article/widgets/opus_content.dart +++ b/lib/pages/article/widgets/opus_content.dart @@ -69,76 +69,78 @@ class OpusContent extends StatelessWidget { switch (element.paraType) { case 1 || 4: final isQuote = element.paraType == 4; - Widget widget = Text.rich( - textAlign: element.align == 1 ? TextAlign.center : null, - TextSpan( - children: element.text?.nodes?.map((item) { - switch (item.type) { - case 'TEXT_NODE_TYPE_RICH' when (item.rich != null): - Rich rich = item.rich!; - switch (rich.type) { - case 'RICH_TEXT_NODE_TYPE_EMOJI': - Emoji emoji = rich.emoji!; - final size = 20.0 * emoji.size; - return WidgetSpan( - child: NetworkImgLayer( - width: size, - height: size, - src: emoji.url, - type: ImageType.emote, - ), - ); - default: - return TextSpan( - text: - '${rich.type == 'RICH_TEXT_NODE_TYPE_WEB' ? '\u{1F517}' : ''}${item.rich!.text}', - style: _getStyle( - rich.style, - rich.type == 'RICH_TEXT_NODE_TYPE_TEXT' - ? null - : colorScheme.primary, - ), - recognizer: TapGestureRecognizer() - ..onTap = () { - switch (rich.type) { - case 'RICH_TEXT_NODE_TYPE_AT': - Get.toNamed('/member?mid=${rich.rid}'); - // case 'RICH_TEXT_NODE_TYPE_TOPIC': - default: - if (rich.jumpUrl != null) { - PiliScheme.routePushFromUrl( - rich.jumpUrl!, - ); - } - } - }, - ); - } - case 'TEXT_NODE_TYPE_FORMULA' when (item.formula != null): - return TextSpan( - children: [ - WidgetSpan( - child: CachedNetworkSVGImage( - height: 65, - 'https://api.bilibili.com/x/web-frontend/mathjax/tex?formula=${Uri.encodeComponent(item.formula!.latexContent!)}', - colorFilter: ColorFilter.mode( - colorScheme.onSurfaceVariant, - BlendMode.srcIn, + Widget widget = SelectionArea( + child: Text.rich( + textAlign: element.align == 1 ? TextAlign.center : null, + TextSpan( + children: element.text?.nodes?.map((item) { + switch (item.type) { + case 'TEXT_NODE_TYPE_RICH' when (item.rich != null): + Rich rich = item.rich!; + switch (rich.type) { + case 'RICH_TEXT_NODE_TYPE_EMOJI': + Emoji emoji = rich.emoji!; + final size = 20.0 * emoji.size; + return WidgetSpan( + child: NetworkImgLayer( + width: size, + height: size, + src: emoji.url, + type: ImageType.emote, + ), + ); + default: + return TextSpan( + text: + '${rich.type == 'RICH_TEXT_NODE_TYPE_WEB' ? '\u{1F517}' : ''}${item.rich!.text}', + style: _getStyle( + rich.style, + rich.type == 'RICH_TEXT_NODE_TYPE_TEXT' + ? null + : colorScheme.primary, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + switch (rich.type) { + case 'RICH_TEXT_NODE_TYPE_AT': + Get.toNamed('/member?mid=${rich.rid}'); + // case 'RICH_TEXT_NODE_TYPE_TOPIC': + default: + if (rich.jumpUrl != null) { + PiliScheme.routePushFromUrl( + rich.jumpUrl!, + ); + } + } + }, + ); + } + case 'TEXT_NODE_TYPE_FORMULA' when (item.formula != null): + return TextSpan( + children: [ + WidgetSpan( + child: CachedNetworkSVGImage( + height: 65, + 'https://api.bilibili.com/x/web-frontend/mathjax/tex?formula=${Uri.encodeComponent(item.formula!.latexContent!)}', + colorFilter: ColorFilter.mode( + colorScheme.onSurfaceVariant, + BlendMode.srcIn, + ), + alignment: Alignment.centerLeft, + placeholderBuilder: (_) => + const SizedBox.shrink(), ), - alignment: Alignment.centerLeft, - placeholderBuilder: (_) => - const SizedBox.shrink(), ), - ), - ], - ); - default: - return _getSpan( - item.word, - isQuote ? colorScheme.onSurfaceVariant : null, - ); - } - }).toList()), + ], + ); + default: + return _getSpan( + item.word, + isQuote ? colorScheme.onSurfaceVariant : null, + ); + } + }).toList()), + ), ); if (isQuote) { widget = Container( @@ -165,8 +167,7 @@ class OpusContent extends StatelessWidget { final height = width == null || pic.height == null ? null : width * pic.height! / pic.width!; - return SelectionContainer.disabled( - child: Hero( + return Hero( tag: pic.url!, child: GestureDetector( onTap: () { @@ -191,42 +192,42 @@ class OpusContent extends StatelessWidget { ), ), ), - )); + ); } else { - return SelectionContainer.disabled( - child: imageView( - maxWidth, - element.pic!.pics! - .map((e) => - ImageModel(width: 1, height: 1, url: e.url!)) - .toList())); + return imageView( + maxWidth, + element.pic!.pics! + .map( + (e) => ImageModel(width: 1, height: 1, url: e.url!)) + .toList()); } case 3 when (element.line != null): - return SelectionContainer.disabled( - child: CachedNetworkImage( + return CachedNetworkImage( width: maxWidth, fit: BoxFit.contain, height: element.line!.pic!.height?.toDouble(), imageUrl: Utils.thumbnailImgUrl(element.line!.pic!.url!), - )); + ); case 5 when (element.list != null): - return Text.rich( - TextSpan( - children: element.list!.items?.indexed.map((entry) { - return TextSpan( - children: [ - const WidgetSpan( - child: Icon(MdiIcons.circleMedium), - alignment: PlaceholderAlignment.middle, - ), - ...entry.$2.nodes!.map((item) { - return _getSpan(item.word); - }), - if (entry.$1 < element.list!.items!.length - 1) - const TextSpan(text: '\n'), - ], - ); - }).toList(), + return SelectionArea( + child: Text.rich( + TextSpan( + children: element.list!.items?.indexed.map((entry) { + return TextSpan( + children: [ + const WidgetSpan( + child: Icon(MdiIcons.circleMedium), + alignment: PlaceholderAlignment.middle, + ), + ...entry.$2.nodes!.map((item) { + return _getSpan(item.word); + }), + if (entry.$1 < element.list!.items!.length - 1) + const TextSpan(text: '\n'), + ], + ); + }).toList(), + ), ), ); case 6: @@ -527,7 +528,7 @@ class OpusContent extends StatelessWidget { ), ); case 7 when (element.code != null): - final highlight = Highlight() + final Highlight highlight = Highlight() ..registerLanguages(builtinAllLanguages); final HighlightResult result = highlight.highlightAuto( element.code!.content!, @@ -538,7 +539,7 @@ class OpusContent extends StatelessWidget { .replaceAll('language-', '') .replaceAll('like', ''), ]); - final renderer = TextSpanRenderer( + final TextSpanRenderer renderer = TextSpanRenderer( const TextStyle(), builtinAllThemes['github']!); result.render(renderer); return Container( @@ -548,34 +549,40 @@ class OpusContent extends StatelessWidget { color: colorScheme.onInverseSurface, ), width: double.infinity, - child: Text.rich(renderer.span!), + child: SelectionArea(child: Text.rich(renderer.span!)), ); default: debugPrint('unknown type ${element.paraType}'); if (element.text?.nodes?.isNotEmpty == true) { - return Text.rich( - textAlign: element.align == 1 ? TextAlign.center : null, - TextSpan( - children: element.text!.nodes! - .map((item) => _getSpan(item.word)) - .toList()), + return SelectionArea( + child: Text.rich( + textAlign: element.align == 1 ? TextAlign.center : null, + TextSpan( + children: element.text!.nodes! + .map((item) => _getSpan(item.word)) + .toList()), + ), ); } - return Text( - '不支持的类型 (${element.paraType})', - style: const TextStyle( - fontWeight: FontWeight.bold, - color: Colors.red, + return SelectionArea( + child: Text( + '不支持的类型 (${element.paraType})', + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.red, + ), ), ); } } catch (e) { - return Text( - '错误的类型 $e', - style: const TextStyle( - fontWeight: FontWeight.bold, - color: Colors.red, + return SelectionArea( + child: Text( + '错误的类型 $e', + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.red, + ), ), ); } @@ -590,7 +597,7 @@ Widget moduleBlockedItem( BoxDecoration? bgImg() { return moduleBlocked.bgImg == null ? null - : (BoxDecoration( + : BoxDecoration( image: DecorationImage( fit: BoxFit.fill, image: CachedNetworkImageProvider( @@ -601,7 +608,7 @@ Widget moduleBlockedItem( ), ), ), - )); + ); } Widget icon(double width) { @@ -683,8 +690,7 @@ Widget moduleBlockedItem( ), ); } - return SelectionContainer.disabled( - child: Container( + return Container( decoration: bgImg(), padding: const EdgeInsets.all(12), child: Row( @@ -720,7 +726,7 @@ Widget moduleBlockedItem( ), ], ), - )); + ); } Widget opusCollection(ThemeData theme, ModuleCollection item) { diff --git a/lib/pages/article/widgets/read_opus.dart b/lib/pages/article/widgets/read_opus.dart index 0109a0a7..4fb67e27 100644 --- a/lib/pages/article/widgets/read_opus.dart +++ b/lib/pages/article/widgets/read_opus.dart @@ -25,14 +25,13 @@ class ReadOpus extends StatelessWidget { try { final item = ops![index]; if (item.insert is String) { - return Text(item.insert); + return SelectableText(item.insert); } if (item.insert is Insert) { InsertCard card = item.insert.card; if (card.url?.isNotEmpty == true) { - return SelectionContainer.disabled( - child: GestureDetector( + return GestureDetector( onTap: () { switch (item.attributes?.clazz) { case 'article-card card': @@ -61,7 +60,7 @@ class ReadOpus extends StatelessWidget { imageUrl: Utils.thumbnailImgUrl(card.url, 60), ), ), - )); + ); } }