feat(app): новый визуальный язык, верхняя навигация, портфели, создание счетов и ручные события
Тема и общие виджеты (AssetIcon, ServiceMark, HelpTip, глоссарий), верхняя панель TopNav и вкладки Аналитики вместо NavSidebar, иконки PWA. Экраны: портфели, создание брокерского счёта, ручное событие, правка инструмента, Обзор карточками по скоупам и таблица активов, пересчёт метрик с опросом /metrics/status. design-system.md обновлён.
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:fintracker_app/core/theme/app_theme.dart';
|
||||
import 'package:fintracker_app/core/widgets/service_mark.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
Future<ByteData> _render(WidgetTester tester, double size) async {
|
||||
final key = GlobalKey();
|
||||
tester.view.physicalSize = Size(size, size);
|
||||
tester.view.devicePixelRatio = 1;
|
||||
addTearDown(tester.view.reset);
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Center(
|
||||
child: RepaintBoundary(
|
||||
key: key,
|
||||
child: ServiceMark(size: size),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
final boundary =
|
||||
key.currentContext!.findRenderObject()! as RenderRepaintBoundary;
|
||||
return (await tester.runAsync(() async {
|
||||
final image = await boundary.toImage();
|
||||
return image.toByteData(format: ui.ImageByteFormat.rawRgba);
|
||||
}))!;
|
||||
}
|
||||
|
||||
Color _at(ByteData data, int size, int x, int y) {
|
||||
final i = (y * size + x) * 4;
|
||||
return Color.fromARGB(
|
||||
data.getUint8(i + 3),
|
||||
data.getUint8(i),
|
||||
data.getUint8(i + 1),
|
||||
data.getUint8(i + 2),
|
||||
);
|
||||
}
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'a gradient disc with three white bars, and nothing outside the disc',
|
||||
(tester) async {
|
||||
const size = 256;
|
||||
final data = await _render(tester, size.toDouble());
|
||||
|
||||
// scale of the 512 design grid
|
||||
int px(num v) => (v * size / 512).round();
|
||||
|
||||
expect(
|
||||
_at(data, size, 2, 2).a,
|
||||
0,
|
||||
reason: 'the corner is outside the disc',
|
||||
);
|
||||
// the tallest bar (x 316..380, y 131..381): white in its middle
|
||||
expect(_at(data, size, px(348), px(256)), const Color(0xFFFFFFFF));
|
||||
// between two bars the disc shows through, and it is coloured, not white
|
||||
final gap = _at(data, size, px(210), px(300));
|
||||
expect(gap.a, 1);
|
||||
expect(gap.b, greaterThan(gap.r), reason: 'blue-violet, not grey');
|
||||
// the gradient runs from the accent (top left) toward violet (bottom right)
|
||||
final start = _at(data, size, px(90), px(90));
|
||||
final end = _at(data, size, px(430), px(430));
|
||||
expect(end.r, greaterThan(start.r));
|
||||
expect(start.b * 255, greaterThan(200));
|
||||
expect(
|
||||
(start.g - end.g) * 255,
|
||||
greaterThan(40),
|
||||
reason: 'the green channel falls off toward violet',
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test('the near end of the gradient is the app accent', () {
|
||||
expect(AppTheme.seed, const Color(0xFF14AFFF));
|
||||
expect(ServiceMark.violet, isNot(AppTheme.seed));
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user