Frontend UX improvements #37
@@ -1,10 +1,12 @@
 | 
			
		||||
import 'package:anyway/utils/get_first_page.dart';
 | 
			
		||||
import 'package:anyway/utils/load_trips.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:anyway/constants.dart';
 | 
			
		||||
 | 
			
		||||
void main() => runApp(const App());
 | 
			
		||||
 | 
			
		||||
final GlobalKey<ScaffoldMessengerState> rootScaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
 | 
			
		||||
final SavedTrips savedTrips = SavedTrips();
 | 
			
		||||
 | 
			
		||||
class App extends StatelessWidget {
 | 
			
		||||
  const App({super.key});
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,6 @@ import 'package:anyway/main.dart';
 | 
			
		||||
import 'package:anyway/structs/trip.dart';
 | 
			
		||||
import 'package:auto_size_text/auto_size_text.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:shared_preferences/shared_preferences.dart';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class saveButton extends StatefulWidget {
 | 
			
		||||
@@ -19,8 +18,9 @@ class _saveButtonState extends State<saveButton> {
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return ElevatedButton(
 | 
			
		||||
      onPressed: () async {
 | 
			
		||||
        SharedPreferences prefs = await SharedPreferences.getInstance();
 | 
			
		||||
        setState(() => widget.trip.toPrefs(prefs));
 | 
			
		||||
        savedTrips.addTrip(widget.trip);
 | 
			
		||||
        // SharedPreferences prefs = await SharedPreferences.getInstance();
 | 
			
		||||
        // setState(() => widget.trip.toPrefs(prefs));
 | 
			
		||||
        rootScaffoldMessengerKey.currentState!.showSnackBar(
 | 
			
		||||
          SnackBar(
 | 
			
		||||
            content: Text('Trip saved'),
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,12 @@
 | 
			
		||||
import 'package:anyway/pages/current_trip.dart';
 | 
			
		||||
import 'package:anyway/utils/load_trips.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
 | 
			
		||||
import 'package:anyway/structs/trip.dart';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TripsOverview extends StatefulWidget {
 | 
			
		||||
  final Future<List<Trip>> trips;
 | 
			
		||||
  final SavedTrips trips;
 | 
			
		||||
  const TripsOverview({
 | 
			
		||||
    super.key,
 | 
			
		||||
    required this.trips,
 | 
			
		||||
@@ -16,49 +17,34 @@ class TripsOverview extends StatefulWidget {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _TripsOverviewState extends State<TripsOverview> {
 | 
			
		||||
  Widget listBuild (BuildContext context, AsyncSnapshot<List<Trip>> snapshot) {
 | 
			
		||||
  Widget listBuild (BuildContext context, SavedTrips trips) {
 | 
			
		||||
    List<Widget> children;
 | 
			
		||||
    if (snapshot.hasData) {
 | 
			
		||||
      children = List<Widget>.generate(snapshot.data!.length, (index) {
 | 
			
		||||
        Trip trip = snapshot.data![index];
 | 
			
		||||
        return ListTile(
 | 
			
		||||
          title: FutureBuilder(
 | 
			
		||||
            future: trip.cityName,
 | 
			
		||||
            builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
 | 
			
		||||
              if (snapshot.hasData) {
 | 
			
		||||
                return Text("Trip to ${snapshot.data}");
 | 
			
		||||
              } else if (snapshot.hasError) {
 | 
			
		||||
                return Text("Error: ${snapshot.error}");
 | 
			
		||||
              } else {
 | 
			
		||||
                return const Text("Trip to ...");
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
          leading: Icon(Icons.pin_drop),
 | 
			
		||||
          onTap: () {
 | 
			
		||||
            Navigator.of(context).push(
 | 
			
		||||
              MaterialPageRoute(
 | 
			
		||||
                builder: (context) => TripPage(trip: trip)
 | 
			
		||||
              )
 | 
			
		||||
            );
 | 
			
		||||
    List<Trip> items = trips.trips;
 | 
			
		||||
    children = List<Widget>.generate(items.length, (index) {
 | 
			
		||||
      Trip trip = items[index];
 | 
			
		||||
      return ListTile(
 | 
			
		||||
        title: FutureBuilder(
 | 
			
		||||
          future: trip.cityName,
 | 
			
		||||
          builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
 | 
			
		||||
            if (snapshot.hasData) {
 | 
			
		||||
              return Text("Trip to ${snapshot.data}");
 | 
			
		||||
            } else if (snapshot.hasError) {
 | 
			
		||||
              return Text("Error: ${snapshot.error}");
 | 
			
		||||
            } else {
 | 
			
		||||
              return const Text("Trip to ...");
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
        );
 | 
			
		||||
      });
 | 
			
		||||
    } else if (snapshot.hasError) {
 | 
			
		||||
      children = [
 | 
			
		||||
        const Icon(
 | 
			
		||||
          Icons.error_outline,
 | 
			
		||||
          color: Colors.red,
 | 
			
		||||
          size: 60,
 | 
			
		||||
        ),
 | 
			
		||||
        Padding(
 | 
			
		||||
          padding: const EdgeInsets.only(top: 16),
 | 
			
		||||
          child: Text('Error: ${snapshot.error}'),
 | 
			
		||||
        ),
 | 
			
		||||
      ];
 | 
			
		||||
    } else {
 | 
			
		||||
      children = [Center(child: CircularProgressIndicator())];
 | 
			
		||||
    }
 | 
			
		||||
        leading: Icon(Icons.pin_drop),
 | 
			
		||||
        onTap: () {
 | 
			
		||||
          Navigator.of(context).push(
 | 
			
		||||
            MaterialPageRoute(
 | 
			
		||||
              builder: (context) => TripPage(trip: trip)
 | 
			
		||||
            )
 | 
			
		||||
          );
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return ListView(
 | 
			
		||||
      children: children,
 | 
			
		||||
@@ -68,9 +54,11 @@ class _TripsOverviewState extends State<TripsOverview> {
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return FutureBuilder(
 | 
			
		||||
      future: widget.trips,
 | 
			
		||||
      builder: listBuild,
 | 
			
		||||
    return ListenableBuilder(
 | 
			
		||||
      listenable: widget.trips,
 | 
			
		||||
      builder: (BuildContext context, Widget? child) {
 | 
			
		||||
        return listBuild(context, widget.trips);
 | 
			
		||||
      }
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
import 'package:anyway/main.dart';
 | 
			
		||||
import 'package:anyway/modules/help_dialog.dart';
 | 
			
		||||
import 'package:anyway/pages/current_trip.dart';
 | 
			
		||||
import 'package:anyway/pages/settings.dart';
 | 
			
		||||
@@ -38,7 +39,8 @@ class _BasePageState extends State<BasePage> {
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    Future<List<Trip>> trips = loadTrips();
 | 
			
		||||
    savedTrips.loadTrips();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
@@ -98,11 +100,11 @@ class _BasePageState extends State<BasePage> {
 | 
			
		||||
            // through the options in the drawer if there isn't enough vertical
 | 
			
		||||
            // space to fit everything.
 | 
			
		||||
            Expanded(
 | 
			
		||||
              child: TripsOverview(trips: trips),
 | 
			
		||||
              child: TripsOverview(trips: savedTrips),
 | 
			
		||||
            ),
 | 
			
		||||
            ElevatedButton(
 | 
			
		||||
              onPressed: () async {
 | 
			
		||||
                removeAllTripsFromPrefs();
 | 
			
		||||
                savedTrips.clearTrips();
 | 
			
		||||
              },
 | 
			
		||||
              child: const Text('Clear trips'),
 | 
			
		||||
            ),
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,6 @@
 | 
			
		||||
import 'dart:ui';
 | 
			
		||||
 | 
			
		||||
import 'package:anyway/constants.dart';
 | 
			
		||||
import 'package:anyway/modules/onboarding_card.dart';
 | 
			
		||||
import 'package:anyway/pages/new_trip_location.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
@@ -33,32 +36,51 @@ class OnboardingPage extends StatefulWidget {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _OnboardingPageState extends State<OnboardingPage> {
 | 
			
		||||
  final PageController _controller = PageController();
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    final PageController _controller = PageController();
 | 
			
		||||
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      body: Stack(
 | 
			
		||||
        children: [
 | 
			
		||||
          Container(
 | 
			
		||||
            decoration: BoxDecoration(
 | 
			
		||||
              gradient: LinearGradient(
 | 
			
		||||
                colors: [Colors.red, Colors.blue],
 | 
			
		||||
                begin: Alignment.topLeft,
 | 
			
		||||
                end: Alignment.bottomRight,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            child: PageView(
 | 
			
		||||
              controller: _controller,
 | 
			
		||||
              children: List.generate(
 | 
			
		||||
                onboardingCards.length,
 | 
			
		||||
                (index) {
 | 
			
		||||
                  return Container(
 | 
			
		||||
                    alignment: Alignment.center,
 | 
			
		||||
                    child: onboardingCards[index],
 | 
			
		||||
                  );
 | 
			
		||||
                }
 | 
			
		||||
              ),
 | 
			
		||||
          AnimatedBuilder(
 | 
			
		||||
            animation: _controller,
 | 
			
		||||
            builder: (context, child) {
 | 
			
		||||
              return Stack(
 | 
			
		||||
                children: [
 | 
			
		||||
                  Container(
 | 
			
		||||
                    decoration: BoxDecoration(
 | 
			
		||||
                      gradient: LinearGradient(
 | 
			
		||||
                        begin: Alignment.topLeft,
 | 
			
		||||
                        end: Alignment.bottomRight,
 | 
			
		||||
                        colors: APP_GRADIENT.colors,
 | 
			
		||||
                        stops: [
 | 
			
		||||
                          (_controller.hasClients ? _controller.page ?? _controller.initialPage : _controller.initialPage) / onboardingCards.length,
 | 
			
		||||
                          (_controller.hasClients ? _controller.page ?? _controller.initialPage + 1 : _controller.initialPage + 1) / onboardingCards.length,
 | 
			
		||||
                        ],
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                  BackdropFilter(
 | 
			
		||||
                    filter: ImageFilter.blur(sigmaX: 100, sigmaY: 100),
 | 
			
		||||
                    child: Container(
 | 
			
		||||
                      color: Colors.black.withOpacity(0),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                ],
 | 
			
		||||
              );
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
          PageView(
 | 
			
		||||
            controller: _controller,
 | 
			
		||||
            children: List.generate(
 | 
			
		||||
              onboardingCards.length,
 | 
			
		||||
              (index) {
 | 
			
		||||
                return Container(
 | 
			
		||||
                  alignment: Alignment.center,
 | 
			
		||||
                  child: onboardingCards[index],
 | 
			
		||||
                );
 | 
			
		||||
              }
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
@@ -75,10 +97,10 @@ class _OnboardingPageState extends State<OnboardingPage> {
 | 
			
		||||
            _controller.nextPage(duration: Duration(milliseconds: 500), curve: Curves.ease);
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        label: ListenableBuilder(
 | 
			
		||||
          listenable: _controller,
 | 
			
		||||
        label: AnimatedBuilder(
 | 
			
		||||
          animation: _controller,
 | 
			
		||||
          builder: (context, child) {
 | 
			
		||||
            if (_controller.page == onboardingCards.length - 1) {
 | 
			
		||||
            if ((_controller.page ?? _controller.initialPage) == onboardingCards.length - 1) {
 | 
			
		||||
              return Row(
 | 
			
		||||
                children: [
 | 
			
		||||
                  const Text("Start planning!"),
 | 
			
		||||
 
 | 
			
		||||
@@ -113,10 +113,3 @@ LinkedList<Landmark> readLandmarks(SharedPreferences prefs, String? firstUUID) {
 | 
			
		||||
  }
 | 
			
		||||
  return landmarks;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void removeAllTripsFromPrefs () async {
 | 
			
		||||
  SharedPreferences prefs = await SharedPreferences.getInstance();
 | 
			
		||||
  prefs.clear();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,23 +5,37 @@ import 'package:anyway/utils/load_trips.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
 | 
			
		||||
Widget getFirstPage() {
 | 
			
		||||
  Future<List<Trip>> trips = loadTrips();
 | 
			
		||||
  // test if there are any active trips
 | 
			
		||||
  // if there are, return the trip list
 | 
			
		||||
  // if there are not, return the onboarding page
 | 
			
		||||
  return FutureBuilder(
 | 
			
		||||
    future: trips,
 | 
			
		||||
    builder: (context, snapshot) {
 | 
			
		||||
      if (snapshot.hasData) {
 | 
			
		||||
        List<Trip> availableTrips = snapshot.data!;
 | 
			
		||||
        if (availableTrips.isNotEmpty) {
 | 
			
		||||
          return TripPage(trip: availableTrips[0]);
 | 
			
		||||
        } else {
 | 
			
		||||
          return OnboardingPage();
 | 
			
		||||
        }
 | 
			
		||||
  SavedTrips trips = SavedTrips();
 | 
			
		||||
  trips.loadTrips();
 | 
			
		||||
 | 
			
		||||
  return ListenableBuilder(
 | 
			
		||||
    listenable: trips,
 | 
			
		||||
    builder: (BuildContext context, Widget? child) {
 | 
			
		||||
      List<Trip> items = trips.trips;
 | 
			
		||||
      if (items.isNotEmpty) {
 | 
			
		||||
        return TripPage(trip: items[0]);
 | 
			
		||||
      } else {
 | 
			
		||||
        return CircularProgressIndicator();
 | 
			
		||||
        return OnboardingPage();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  );
 | 
			
		||||
  // Future<List<Trip>> trips = loadTrips();
 | 
			
		||||
  // // test if there are any active trips
 | 
			
		||||
  // // if there are, return the trip list
 | 
			
		||||
  // // if there are not, return the onboarding page
 | 
			
		||||
  // return FutureBuilder(
 | 
			
		||||
  //   future: trips,
 | 
			
		||||
  //   builder: (context, snapshot) {
 | 
			
		||||
  //     if (snapshot.hasData) {
 | 
			
		||||
  //       List<Trip> availableTrips = snapshot.data!;
 | 
			
		||||
  //       if (availableTrips.isNotEmpty) {
 | 
			
		||||
  //         return TripPage(trip: availableTrips[0]);
 | 
			
		||||
  //       } else {
 | 
			
		||||
  //         return OnboardingPage();
 | 
			
		||||
  //       }
 | 
			
		||||
  //     } else {
 | 
			
		||||
  //       return CircularProgressIndicator();
 | 
			
		||||
  //     }
 | 
			
		||||
  //   }
 | 
			
		||||
  // );
 | 
			
		||||
}
 | 
			
		||||
@@ -1,16 +1,39 @@
 | 
			
		||||
import 'package:anyway/structs/trip.dart';
 | 
			
		||||
import 'package:shared_preferences/shared_preferences.dart';
 | 
			
		||||
 | 
			
		||||
Future<List<Trip>> loadTrips() async {
 | 
			
		||||
  SharedPreferences prefs = await SharedPreferences.getInstance();
 | 
			
		||||
import 'package:flutter/foundation.dart';
 | 
			
		||||
 | 
			
		||||
  List<Trip> trips = [];
 | 
			
		||||
  Set<String> keys = prefs.getKeys();
 | 
			
		||||
  for (String key in keys) {
 | 
			
		||||
    if (key.startsWith('trip_')) {
 | 
			
		||||
      String uuid = key.replaceFirst('trip_', '');
 | 
			
		||||
      trips.add(Trip.fromPrefs(prefs, uuid));
 | 
			
		||||
class SavedTrips extends ChangeNotifier {
 | 
			
		||||
  List<Trip> _trips = [];
 | 
			
		||||
 | 
			
		||||
  List<Trip> get trips => _trips;
 | 
			
		||||
 | 
			
		||||
  void loadTrips() async {
 | 
			
		||||
    SharedPreferences prefs = await SharedPreferences.getInstance();
 | 
			
		||||
 | 
			
		||||
    List<Trip> trips = [];
 | 
			
		||||
    Set<String> keys = prefs.getKeys();
 | 
			
		||||
    for (String key in keys) {
 | 
			
		||||
      if (key.startsWith('trip_')) {
 | 
			
		||||
        String uuid = key.replaceFirst('trip_', '');
 | 
			
		||||
        trips.add(Trip.fromPrefs(prefs, uuid));
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    _trips = trips;
 | 
			
		||||
    notifyListeners();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void addTrip(Trip trip) async {
 | 
			
		||||
    SharedPreferences prefs = await SharedPreferences.getInstance();
 | 
			
		||||
    trip.toPrefs(prefs);
 | 
			
		||||
    _trips.add(trip);
 | 
			
		||||
    notifyListeners();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void clearTrips () async {
 | 
			
		||||
    SharedPreferences prefs = await SharedPreferences.getInstance();
 | 
			
		||||
    prefs.clear();
 | 
			
		||||
    _trips = [];
 | 
			
		||||
    notifyListeners();
 | 
			
		||||
  }
 | 
			
		||||
  return trips;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user