diff --git a/Flutter_mobile/sample_todo_app/lib/main.dart b/Flutter_mobile/sample_todo_app/lib/main.dart index 375d9fb..ad38242 100644 --- a/Flutter_mobile/sample_todo_app/lib/main.dart +++ b/Flutter_mobile/sample_todo_app/lib/main.dart @@ -8,8 +8,7 @@ import 'package:statsig/statsig.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); - await Statsig.initialize( - statsigApiKey, StatsigUser(userId: "flutter_dummy_user_id")); + await Statsig.initialize(statsigApiKey, null); runApp(const ProviderScope(child: MainApp())); } @@ -18,56 +17,6 @@ class MainApp extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - return const MaterialApp( - home: DetectLifecycle(), - ); - } -} - -class DetectLifecycle extends ConsumerStatefulWidget { - const DetectLifecycle({Key? key}) : super(key: key); - - @override - ConsumerState createState() { - return _DetectLifecycleState(); - } -} - -class _DetectLifecycleState extends ConsumerState - with WidgetsBindingObserver { - final appOpened = "CLIENT_TODO_APP_OPENED"; - final appBackgrounded = "CLIENT_TODO_APP_BACKGROUND"; - - @override - void initState() { - WidgetsBinding.instance.addObserver(this); - super.initState(); - } - - @override - void dispose() { - WidgetsBinding.instance.removeObserver(this); - super.dispose(); - } - - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - switch (state) { - case AppLifecycleState.resumed: - Statsig.logEvent(appOpened); - break; - case AppLifecycleState.inactive: - Statsig.logEvent(appBackgrounded); - break; - case AppLifecycleState.paused: - case AppLifecycleState.detached: - case AppLifecycleState.hidden: - break; - } - } - - @override - Widget build(BuildContext context) { final goRouter = ref.watch(goRouterProvider); return DynamicColorBuilder( builder: (lightColorScheme, darkColorScheme) => MaterialApp.router( diff --git a/Flutter_mobile/sample_todo_app/lib/repositories/todo_repository.dart b/Flutter_mobile/sample_todo_app/lib/repositories/todo_repository.dart index e3d92ea..3b9c807 100644 --- a/Flutter_mobile/sample_todo_app/lib/repositories/todo_repository.dart +++ b/Flutter_mobile/sample_todo_app/lib/repositories/todo_repository.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:statsig/statsig.dart'; import '../models/todo.dart'; import '../network/network_api.dart'; @@ -12,14 +13,36 @@ class TodoRepository extends StateNotifier> { } Future loadTodos() async { + final sortingVal = + Statsig.getExperiment("item_sorting").get("sort_order", 0); final todoList = await fetchTodoList(); + switch (sortingVal) { + case 1: + todoList.sort((a, b) { + return a.task.toLowerCase().compareTo(b.task.toLowerCase()); + }); + break; + case 2: + todoList.sort((a, b) { + return a.createdDate.compareTo(b.createdDate); + }); + break; + case 3: + todoList.sort((a, b) { + return b.createdDate.compareTo(a.createdDate); + }); + break; + default: + todoList; + break; + } state = todoList; } Future saveTodos(List todos) async { final prefs = await SharedPreferences.getInstance(); final encodedTodos = - jsonEncode(todos.map((todo) => todo.toJson()).toList()); + jsonEncode(todos.map((todo) => todo.toJson()).toList()); await prefs.setString('todos', encodedTodos); } @@ -69,8 +92,7 @@ class TodoRepository extends StateNotifier> { void editTodo(String id, String title) { state = [ for (final todo in state) - if (todo.id == int.parse(id)) todo.copyWith(task: title) else - todo + if (todo.id == int.parse(id)) todo.copyWith(task: title) else todo ]; } @@ -86,7 +108,7 @@ class TodoRepository extends StateNotifier> { } final todoRepositoryProvider = -StateNotifierProvider>((ref) { + StateNotifierProvider>((ref) { return TodoRepository(); }); diff --git a/Flutter_mobile/sample_todo_app/lib/routing/go_router.dart b/Flutter_mobile/sample_todo_app/lib/routing/go_router.dart index 23c90a2..2b0ab19 100644 --- a/Flutter_mobile/sample_todo_app/lib/routing/go_router.dart +++ b/Flutter_mobile/sample_todo_app/lib/routing/go_router.dart @@ -1,5 +1,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; +import 'package:sample_todo_app/screens/login_screen.dart'; import 'package:sample_todo_app/screens/tasks/add_todo_screen.dart'; import 'package:sample_todo_app/screens/tasks/tasks_screen.dart'; @@ -7,6 +8,7 @@ import '../models/todo.dart'; import '../screens/home_screen.dart'; enum AppRoute { + loginScreen, homeScreen, addTodoScreen, tasksScreen, @@ -14,8 +16,13 @@ enum AppRoute { final goRouterProvider = Provider((ref) { return GoRouter( - initialLocation: '/home', + initialLocation: '/login', routes: [ + GoRoute( + name: AppRoute.loginScreen.name, + path: '/login', + builder: (context, state) => const LoginScreen(), + ), GoRoute( name: AppRoute.homeScreen.name, path: '/home', diff --git a/Flutter_mobile/sample_todo_app/lib/screens/home_screen.dart b/Flutter_mobile/sample_todo_app/lib/screens/home_screen.dart index 6a55d3a..beaa655 100644 --- a/Flutter_mobile/sample_todo_app/lib/screens/home_screen.dart +++ b/Flutter_mobile/sample_todo_app/lib/screens/home_screen.dart @@ -1,12 +1,61 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:sample_todo_app/screens/tasks/tasks_screen.dart'; +import 'package:statsig/statsig.dart'; class HomeScreen extends ConsumerWidget { const HomeScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { + return const DetectLifecycle(); + } +} + +class DetectLifecycle extends ConsumerStatefulWidget { + const DetectLifecycle({Key? key}) : super(key: key); + + @override + ConsumerState createState() { + return _DetectLifecycleState(); + } +} + +class _DetectLifecycleState extends ConsumerState + with WidgetsBindingObserver { + final appOpened = "CLIENT_TODO_APP_OPENED"; + final appBackgrounded = "CLIENT_TODO_APP_BACKGROUND"; + + @override + void initState() { + WidgetsBinding.instance.addObserver(this); + super.initState(); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + switch (state) { + case AppLifecycleState.resumed: + Statsig.logEvent(appOpened); + break; + case AppLifecycleState.inactive: + Statsig.logEvent(appBackgrounded); + break; + case AppLifecycleState.paused: + case AppLifecycleState.detached: + case AppLifecycleState.hidden: + break; + } + } + + @override + Widget build(BuildContext context) { return Scaffold( body: [const TasksScreen()][0], ); diff --git a/Flutter_mobile/sample_todo_app/lib/screens/login_screen.dart b/Flutter_mobile/sample_todo_app/lib/screens/login_screen.dart new file mode 100644 index 0000000..40a673d --- /dev/null +++ b/Flutter_mobile/sample_todo_app/lib/screens/login_screen.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:sample_todo_app/routing/go_router.dart'; +import 'package:statsig/statsig.dart'; + +class LoginScreen extends StatefulWidget { + const LoginScreen({super.key}); + + @override + State createState() => _LoginScreenState(); +} + +class _LoginScreenState extends State { + bool isLoading = false; + + void updateStatsigUser(BuildContext context) async { + setState(() { + isLoading = true; + }); + await Statsig.updateUser(StatsigUser(userId: "flutter_dummy_user_id")); + Future.delayed(const Duration(milliseconds: 3000), () { + setState(() { + isLoading = false; + }); + context.pushNamed(AppRoute.homeScreen.name); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: SingleChildScrollView( + child: Column( + children: [ + const Padding( + padding: + EdgeInsets.only(left: 30.0, right: 30.0, top: 220, bottom: 0), + child: TextField( + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: 'Username', + ), + ), + ), + const Padding( + padding: + EdgeInsets.only(left: 30.0, right: 30.0, top: 15, bottom: 0), + child: TextField( + obscureText: true, + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: 'Password', + ), + ), + ), + SizedBox( + height: 65, + width: 355, + child: Padding( + padding: const EdgeInsets.only(top: 20.0), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.blue, // background + onPrimary: Colors.white, // foreground + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5), + )), + onPressed: () { + updateStatsigUser(context); + }, + child: const Text( + 'Log in ', + style: TextStyle(color: Colors.white, fontSize: 20), + ), + ), + ), + ), + Visibility( + visible: isLoading, + child: const Padding( + padding: EdgeInsets.only(top: 30), + child: SizedBox( + height: 50, + width: 50, + child: CircularProgressIndicator(), + ))) + ], + ), + ), + ); + } +}