info: improved layout

This commit is contained in:
Thibault Deckers 2020-11-19 10:54:41 +09:00
parent ba031a0144
commit ced2861860

View file

@ -1,6 +1,9 @@
import 'dart:math';
import 'package:aves/widgets/common/aves_filter_chip.dart'; import 'package:aves/widgets/common/aves_filter_chip.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class SectionRow extends StatelessWidget { class SectionRow extends StatelessWidget {
final IconData icon; final IconData icon;
@ -54,36 +57,71 @@ class _InfoRowGroupState extends State<InfoRowGroup> {
int get maxValueLength => widget.maxValueLength; int get maxValueLength => widget.maxValueLength;
static const keyValuePadding = 16;
static final baseStyle = TextStyle(fontFamily: 'Concourse');
static final keyStyle = baseStyle.copyWith(color: Colors.white70, height: 1.7);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (keyValues.isEmpty) return SizedBox.shrink(); if (keyValues.isEmpty) return SizedBox.shrink();
// compute the size of keys and space in order to align values
final textScaleFactor = MediaQuery.textScaleFactorOf(context);
final keySizes = Map.fromEntries(keyValues.keys.map((key) => MapEntry(key, _getSpanWidth(TextSpan(text: '$key', style: keyStyle), textScaleFactor))));
final baseSpaceWidth = _getSpanWidth(TextSpan(text: '\u200A' * 100, style: baseStyle), textScaleFactor);
final lastKey = keyValues.keys.last; final lastKey = keyValues.keys.last;
return Column( return LayoutBuilder(
crossAxisAlignment: CrossAxisAlignment.start, builder: (context, constraints) {
children: [ // find longest key below threshold
SelectableText.rich( final maxBaseValueX = constraints.maxWidth / 3;
TextSpan( final baseValueX = keySizes.values.where((size) => size < maxBaseValueX).fold(0.0, max);
children: keyValues.entries.expand(
(kv) { return Column(
final key = kv.key; crossAxisAlignment: CrossAxisAlignment.start,
var value = kv.value; children: [
final showPreviewOnly = maxValueLength > 0 && value.length > maxValueLength && !_expandedKeys.contains(key); SelectableText.rich(
if (showPreviewOnly) { TextSpan(
value = '${value.substring(0, maxValueLength)}'; children: keyValues.entries.expand(
} (kv) {
return [ final key = kv.key;
TextSpan(text: '$key ', style: TextStyle(color: Colors.white70, height: 1.7)), var value = kv.value;
TextSpan(text: '$value${key == lastKey ? '' : '\n'}', recognizer: showPreviewOnly ? _buildTapRecognizer(key) : null), // long values are clipped, and made expandable by tapping them
]; final showPreviewOnly = maxValueLength > 0 && value.length > maxValueLength && !_expandedKeys.contains(key);
}, if (showPreviewOnly) {
).toList(), value = '${value.substring(0, maxValueLength)}';
), }
style: TextStyle(fontFamily: 'Concourse'),
), // as of Flutter v1.22.4, `SelectableText` cannot contain `WidgetSpan`
], // so we add padding using multiple hair spaces instead
final thisSpaceSize = max(0.0, (baseValueX - keySizes[key])) + keyValuePadding;
final spaceCount = (100 * thisSpaceSize / baseSpaceWidth).round();
return [
TextSpan(text: '$key', style: keyStyle),
TextSpan(text: '\u200A' * spaceCount),
TextSpan(text: '$value${key == lastKey ? '' : '\n'}', recognizer: showPreviewOnly ? _buildTapRecognizer(key) : null),
];
},
).toList(),
),
style: baseStyle,
),
],
);
},
); );
} }
double _getSpanWidth(TextSpan span, double textScaleFactor) {
final para = RenderParagraph(
span,
textDirection: TextDirection.ltr,
textScaleFactor: textScaleFactor,
)..layout(BoxConstraints(), parentUsesSize: true);
return para.getMaxIntrinsicWidth(double.infinity);
}
GestureRecognizer _buildTapRecognizer(String key) { GestureRecognizer _buildTapRecognizer(String key) {
return TapGestureRecognizer()..onTap = () => setState(() => _expandedKeys.add(key)); return TapGestureRecognizer()..onTap = () => setState(() => _expandedKeys.add(key));
} }