commit from livecd
This commit is contained in:
@@ -2,15 +2,41 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:mileograph_flutter/services/authservice.dart';
|
import 'package:mileograph_flutter/services/authservice.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class LoginPage extends StatelessWidget {
|
class LoginScreen extends StatelessWidget {
|
||||||
const LoginPage({super.key});
|
const LoginScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Scaffold(
|
||||||
|
resizeToAvoidBottomInset: true,
|
||||||
|
body: Container(
|
||||||
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
|
spacing: 50,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [LoginPanel()],
|
children: [
|
||||||
|
Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
children: [
|
||||||
|
TextSpan(text: "Mile"),
|
||||||
|
TextSpan(
|
||||||
|
text: "O",
|
||||||
|
style: TextStyle(color: Colors.red),
|
||||||
|
),
|
||||||
|
TextSpan(text: "graph"),
|
||||||
|
],
|
||||||
|
style: TextStyle(
|
||||||
|
decoration: TextDecoration.none,
|
||||||
|
color: Colors.white,
|
||||||
|
fontFamily: "Tomatoes",
|
||||||
|
fontSize: 50,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
LoginPanel(),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -99,6 +125,7 @@ class _LoginPanelContentState extends State<LoginPanelContent> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
@@ -149,6 +176,7 @@ class RegisterPanelContent extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
|
|||||||
@@ -1,19 +1,36 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:mileograph_flutter/services/apiService.dart';
|
import 'package:mileograph_flutter/services/apiService.dart';
|
||||||
import 'package:mileograph_flutter/services/authservice.dart';
|
import 'package:mileograph_flutter/services/authservice.dart';
|
||||||
|
import 'package:mileograph_flutter/services/dataService.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'components/login/login.dart';
|
import 'components/login/login.dart';
|
||||||
|
|
||||||
|
late ApiService api;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(
|
runApp(
|
||||||
MultiProvider(
|
MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
Provider<ApiService>(
|
Provider<ApiService>(
|
||||||
create: (_) =>
|
create: (_) {
|
||||||
ApiService(baseUrl: 'https://dev.mileograph.co.uk/api/v1'),
|
api = ApiService(baseUrl: 'https://dev.mileograph.co.uk/api/v1');
|
||||||
|
return api;
|
||||||
|
},
|
||||||
),
|
),
|
||||||
ChangeNotifierProvider<AuthService>(
|
ChangeNotifierProxyProvider<ApiService, AuthService>(
|
||||||
create: (context) => AuthService(api: context.read<ApiService>()),
|
create: (context) => AuthService(api: context.read<ApiService>()),
|
||||||
|
update: (_, api, previous) {
|
||||||
|
return previous ?? AuthService(api: api);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ProxyProvider<AuthService, void>(
|
||||||
|
update: (_, auth, __) {
|
||||||
|
api.setTokenProvider(() => auth.token);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ChangeNotifierProxyProvider<ApiService, DataService>(
|
||||||
|
create: (context) => DataService(api: context.read<ApiService>()),
|
||||||
|
update: (_, api, previous) => previous ?? DataService(api: api),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: MyApp(),
|
child: MyApp(),
|
||||||
@@ -21,25 +38,29 @@ void main() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AppRoot extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Consumer<AuthService>(
|
||||||
|
builder: (context, auth, child) {
|
||||||
|
return auth.isLoggedIn
|
||||||
|
? MyHomePage(title: "Mileograph")
|
||||||
|
: LoginScreen();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
const MyApp({super.key});
|
const MyApp({super.key});
|
||||||
|
|
||||||
// This widget is the root of your application.
|
// This widget is the root of your application.
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final auth = context.read<AuthService>();
|
|
||||||
|
|
||||||
Widget fullPage;
|
|
||||||
Widget subPage;
|
|
||||||
if (!auth.isLoggedIn) {
|
|
||||||
fullPage = LoginPage();
|
|
||||||
} else {
|
|
||||||
fullPage = MyHomePage(title: "Mileograph");
|
|
||||||
}
|
|
||||||
|
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: 'Flutter Demo',
|
title: 'Flutter Demo',
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
|
useMaterial3: true,
|
||||||
// This is the theme of your application.
|
// This is the theme of your application.
|
||||||
//
|
//
|
||||||
// TRY THIS: Try running your application with "flutter run". You'll see
|
// TRY THIS: Try running your application with "flutter run". You'll see
|
||||||
@@ -48,7 +69,7 @@ class MyApp extends StatelessWidget {
|
|||||||
// and then invoke "hot reload" (save your changes or press the "hot
|
// and then invoke "hot reload" (save your changes or press the "hot
|
||||||
// reload" button in a Flutter-supported IDE, or press "r" if you used
|
// reload" button in a Flutter-supported IDE, or press "r" if you used
|
||||||
// the command line to start the app).
|
// the command line to start the app).
|
||||||
//
|
//fullPage
|
||||||
// Notice that the counter didn't reset back to zero; the application
|
// Notice that the counter didn't reset back to zero; the application
|
||||||
// state is not lost during the reload. To reset the state, use hot
|
// state is not lost during the reload. To reset the state, use hot
|
||||||
// restart instead.
|
// restart instead.
|
||||||
@@ -58,13 +79,14 @@ class MyApp extends StatelessWidget {
|
|||||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blueGrey),
|
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blueGrey),
|
||||||
),
|
),
|
||||||
darkTheme: ThemeData(
|
darkTheme: ThemeData(
|
||||||
|
useMaterial3: true,
|
||||||
colorScheme: ColorScheme.fromSeed(
|
colorScheme: ColorScheme.fromSeed(
|
||||||
seedColor: Colors.blueGrey,
|
seedColor: Colors.blueGrey,
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
themeMode: ThemeMode.system,
|
themeMode: ThemeMode.system,
|
||||||
home: const MyHomePage(title: 'Mile-O-Graph'),
|
home: AppRoot(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,10 +111,41 @@ class MyHomePage extends StatefulWidget {
|
|||||||
|
|
||||||
class _MyHomePageState extends State<MyHomePage> {
|
class _MyHomePageState extends State<MyHomePage> {
|
||||||
int pageIndex = 0;
|
int pageIndex = 0;
|
||||||
|
final List<Widget> contentPages = [
|
||||||
|
Dashboard(),
|
||||||
|
Center(child: Text("Calculator Page")),
|
||||||
|
Center(child: Text("Entries Page")),
|
||||||
|
Center(child: Text("Traction Page")),
|
||||||
|
Center(child: Text("Trips Page")),
|
||||||
|
];
|
||||||
|
|
||||||
bool loggedIn = false;
|
bool loggedIn = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
final data = context.read<DataService>();
|
||||||
|
if (data.homepageStats == null) {
|
||||||
|
data.fetchHomepageStats();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
Widget currentPage;
|
||||||
|
|
||||||
|
final data = context.watch<DataService>();
|
||||||
|
|
||||||
|
if (data.homepageStats != null) {
|
||||||
|
currentPage = contentPages[pageIndex];
|
||||||
|
} else {
|
||||||
|
currentPage = Center(
|
||||||
|
child: FilledButton(
|
||||||
|
onPressed: data.fetchHomepageStats,
|
||||||
|
child: Text("Fetch"),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
// This method is rerun every time setState is called, for instance as done
|
// This method is rerun every time setState is called, for instance as done
|
||||||
// by the _incrementCounter method above.
|
// by the _incrementCounter method above.
|
||||||
//
|
//
|
||||||
@@ -108,10 +161,8 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||||||
// Here we take the value from the MyHomePage object that was created by
|
// Here we take the value from the MyHomePage object that was created by
|
||||||
// the App.build method, and use it to set our appbar title.
|
// the App.build method, and use it to set our appbar title.
|
||||||
title: Text(widget.title),
|
title: Text(widget.title),
|
||||||
leading: IconButton(onPressed: null, icon: Icon(Icons.menu)),
|
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(onPressed: null, icon: Icon(Icons.account_circle)),
|
IconButton(onPressed: null, icon: Icon(Icons.account_circle)),
|
||||||
IconButton(onPressed: null, icon: Icon(Icons.settings)),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
bottomNavigationBar: NavigationBar(
|
bottomNavigationBar: NavigationBar(
|
||||||
@@ -123,16 +174,13 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||||||
},
|
},
|
||||||
destinations: [
|
destinations: [
|
||||||
NavigationDestination(icon: Icon(Icons.home), label: "Home"),
|
NavigationDestination(icon: Icon(Icons.home), label: "Home"),
|
||||||
|
NavigationDestination(icon: Icon(Icons.route), label: "Calculator"),
|
||||||
NavigationDestination(icon: Icon(Icons.list), label: "Entries"),
|
NavigationDestination(icon: Icon(Icons.list), label: "Entries"),
|
||||||
NavigationDestination(icon: Icon(Icons.train), label: "Traction"),
|
NavigationDestination(icon: Icon(Icons.train), label: "Traction"),
|
||||||
NavigationDestination(icon: Icon(Icons.route), label: "Trips"),
|
NavigationDestination(icon: Icon(Icons.book), label: "Trips"),
|
||||||
NavigationDestination(
|
|
||||||
icon: Icon(Icons.account_circle),
|
|
||||||
label: "User",
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: Dashboard(),
|
body: currentPage,
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
onPressed: null,
|
onPressed: null,
|
||||||
tooltip: 'New Entry',
|
tooltip: 'New Entry',
|
||||||
|
|||||||
@@ -34,3 +34,120 @@ class AuthenticatedUserData extends UserData {
|
|||||||
|
|
||||||
final String access_token;
|
final String access_token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class HomepageStats {
|
||||||
|
final double totalMileage;
|
||||||
|
final List<YearlyMileage> yearlyMileage;
|
||||||
|
final List<LocoSummary> topLocos;
|
||||||
|
final List<LeaderboardEntry> leaderboard;
|
||||||
|
final List<TripSummary> trips;
|
||||||
|
|
||||||
|
HomepageStats({
|
||||||
|
required this.totalMileage,
|
||||||
|
required this.yearlyMileage,
|
||||||
|
required this.topLocos,
|
||||||
|
required this.leaderboard,
|
||||||
|
required this.trips,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory HomepageStats.fromJson(Map<String, dynamic> json) {
|
||||||
|
return HomepageStats(
|
||||||
|
totalMileage: (json['milage_data']['mileage'] as num).toDouble(),
|
||||||
|
yearlyMileage: (json['yearly_mileage'] as List)
|
||||||
|
.map((e) => YearlyMileage.fromJson(e))
|
||||||
|
.toList(),
|
||||||
|
topLocos: (json['top_locos'] as List)
|
||||||
|
.map((e) => LocoSummary.fromJson(e))
|
||||||
|
.toList(),
|
||||||
|
leaderboard: (json['leaderboard_data'] as List)
|
||||||
|
.map((e) => LeaderboardEntry.fromJson(e))
|
||||||
|
.toList(),
|
||||||
|
trips: (json['trip_data'] as List)
|
||||||
|
.map((e) => TripSummary.fromJson(e))
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class YearlyMileage {
|
||||||
|
final int? year;
|
||||||
|
final double mileage;
|
||||||
|
|
||||||
|
YearlyMileage({this.year, required this.mileage});
|
||||||
|
|
||||||
|
factory YearlyMileage.fromJson(Map<String, dynamic> json) => YearlyMileage(
|
||||||
|
year: json['year'],
|
||||||
|
mileage: (json['mileage'] as num).toDouble(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class LocoSummary {
|
||||||
|
final String locoType, locoClass, locoNumber, locoName, locoOperator;
|
||||||
|
final String? locoNotes, locoEvn;
|
||||||
|
final int locoId, locoJourneys;
|
||||||
|
final double locoMileage;
|
||||||
|
|
||||||
|
LocoSummary({
|
||||||
|
required this.locoType,
|
||||||
|
required this.locoClass,
|
||||||
|
required this.locoNumber,
|
||||||
|
required this.locoName,
|
||||||
|
required this.locoOperator,
|
||||||
|
this.locoNotes,
|
||||||
|
this.locoEvn,
|
||||||
|
required this.locoId,
|
||||||
|
required this.locoMileage,
|
||||||
|
required this.locoJourneys,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory LocoSummary.fromJson(Map<String, dynamic> json) => LocoSummary(
|
||||||
|
locoType: json['loco_type'],
|
||||||
|
locoClass: json['loco_class'],
|
||||||
|
locoNumber: json['loco_number'],
|
||||||
|
locoName: json['loco_name'],
|
||||||
|
locoOperator: json['loco_operator'],
|
||||||
|
locoNotes: json['loco_notes'],
|
||||||
|
locoEvn: json['loco_evn'],
|
||||||
|
locoId: json['loco_id'],
|
||||||
|
locoMileage: (json['loco_mileage'] as num).toDouble(),
|
||||||
|
locoJourneys: json['loco_journeys'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class LeaderboardEntry {
|
||||||
|
final String userId, username, userFullName;
|
||||||
|
final double mileage;
|
||||||
|
|
||||||
|
LeaderboardEntry({
|
||||||
|
required this.userId,
|
||||||
|
required this.username,
|
||||||
|
required this.userFullName,
|
||||||
|
required this.mileage,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory LeaderboardEntry.fromJson(Map<String, dynamic> json) =>
|
||||||
|
LeaderboardEntry(
|
||||||
|
userId: json['user_id'],
|
||||||
|
username: json['username'],
|
||||||
|
userFullName: json['user_full_name'],
|
||||||
|
mileage: (json['mileage'] as num).toDouble(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class TripSummary {
|
||||||
|
final int tripId;
|
||||||
|
final String tripName;
|
||||||
|
final double tripMileage;
|
||||||
|
|
||||||
|
TripSummary({
|
||||||
|
required this.tripId,
|
||||||
|
required this.tripName,
|
||||||
|
required this.tripMileage,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory TripSummary.fromJson(Map<String, dynamic> json) => TripSummary(
|
||||||
|
tripId: json['trip_id'],
|
||||||
|
tripName: json['trip_name'],
|
||||||
|
tripMileage: (json['trip_mileage'] as num).toDouble(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,15 +1,32 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:mileograph_flutter/services/authservice.dart';
|
||||||
|
|
||||||
|
typedef TokenProvider = String? Function();
|
||||||
|
|
||||||
class ApiService {
|
class ApiService {
|
||||||
final String baseUrl;
|
final String baseUrl;
|
||||||
|
TokenProvider? _getToken;
|
||||||
|
|
||||||
ApiService({required this.baseUrl});
|
ApiService({required this.baseUrl});
|
||||||
|
|
||||||
|
void setTokenProvider(TokenProvider provider) {
|
||||||
|
_getToken = provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> _buildHeaders(Map<String, String>? extra) {
|
||||||
|
final token = _getToken?.call();
|
||||||
|
final headers = {'accept': 'application/json', ...?extra};
|
||||||
|
if (token != null && token.isNotEmpty) {
|
||||||
|
headers['Authorization'] = 'Bearer $token';
|
||||||
|
}
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
Future<dynamic> get(String endpoint, {Map<String, String>? headers}) async {
|
Future<dynamic> get(String endpoint, {Map<String, String>? headers}) async {
|
||||||
final response = await http.get(
|
final response = await http.get(
|
||||||
Uri.parse('$baseUrl$endpoint'),
|
Uri.parse('$baseUrl$endpoint'),
|
||||||
headers: headers,
|
headers: _buildHeaders(headers),
|
||||||
);
|
);
|
||||||
return _processResponse(response);
|
return _processResponse(response);
|
||||||
}
|
}
|
||||||
@@ -21,7 +38,7 @@ class ApiService {
|
|||||||
}) async {
|
}) async {
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
Uri.parse('$baseUrl$endpoint'),
|
Uri.parse('$baseUrl$endpoint'),
|
||||||
headers: _jsonHeaders(headers),
|
headers: _buildHeaders(_jsonHeaders(headers)),
|
||||||
body: jsonEncode(data),
|
body: jsonEncode(data),
|
||||||
);
|
);
|
||||||
return _processResponse(response);
|
return _processResponse(response);
|
||||||
@@ -30,10 +47,10 @@ class ApiService {
|
|||||||
Future<dynamic> postForm(String endpoint, Map<String, String> data) async {
|
Future<dynamic> postForm(String endpoint, Map<String, String> data) async {
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
Uri.parse('$baseUrl$endpoint'),
|
Uri.parse('$baseUrl$endpoint'),
|
||||||
headers: {
|
headers: _buildHeaders({
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
'accept': 'application/json',
|
'accept': 'application/json',
|
||||||
},
|
}),
|
||||||
body: data, // http package handles form-encoding for Map<String, String>
|
body: data, // http package handles form-encoding for Map<String, String>
|
||||||
);
|
);
|
||||||
return _processResponse(response);
|
return _processResponse(response);
|
||||||
@@ -46,7 +63,7 @@ class ApiService {
|
|||||||
}) async {
|
}) async {
|
||||||
final response = await http.put(
|
final response = await http.put(
|
||||||
Uri.parse('$baseUrl$endpoint'),
|
Uri.parse('$baseUrl$endpoint'),
|
||||||
headers: _jsonHeaders(headers),
|
headers: _buildHeaders(_jsonHeaders(headers)),
|
||||||
body: jsonEncode(data),
|
body: jsonEncode(data),
|
||||||
);
|
);
|
||||||
return _processResponse(response);
|
return _processResponse(response);
|
||||||
@@ -58,7 +75,7 @@ class ApiService {
|
|||||||
}) async {
|
}) async {
|
||||||
final response = await http.delete(
|
final response = await http.delete(
|
||||||
Uri.parse('$baseUrl$endpoint'),
|
Uri.parse('$baseUrl$endpoint'),
|
||||||
headers: headers,
|
headers: _buildHeaders(headers),
|
||||||
);
|
);
|
||||||
return _processResponse(response);
|
return _processResponse(response);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,10 @@ flutter:
|
|||||||
# included with your application, so that you can use the icons in
|
# included with your application, so that you can use the icons in
|
||||||
# the material Icons class.
|
# the material Icons class.
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
fonts:
|
||||||
|
- family: Tomatoes
|
||||||
|
fonts:
|
||||||
|
- asset: lib/assets/fonts/Tomatoes-O8L8.ttf
|
||||||
# To add assets to your application, add an assets section, like this:
|
# To add assets to your application, add an assets section, like this:
|
||||||
# assets:
|
# assets:
|
||||||
# - images/a_dot_burr.jpeg
|
# - images/a_dot_burr.jpeg
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
import 'package:mileograph_sbb_flutter/main.dart';
|
import 'package:mileograph_flutter/main.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||||
|
|||||||
Reference in New Issue
Block a user