From 4119e497a90887b30dc1103238365de95828dbd5 Mon Sep 17 00:00:00 2001 From: lttsNilesh Date: Wed, 29 Nov 2023 14:00:58 +0530 Subject: [PATCH 1/5] sort via experiment --- .../lib/repositories/todo_repository.dart | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) 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(); }); From 29c1b94fd6a2249b7fc565943574c42791d7e358 Mon Sep 17 00:00:00 2001 From: lttsNilesh Date: Thu, 30 Nov 2023 11:13:01 +0530 Subject: [PATCH 2/5] Login screen added with navigation. --- .../lib/routing/go_router.dart | 9 ++- .../lib/screens/login_screen.dart | 71 +++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 Flutter_mobile/sample_todo_app/lib/screens/login_screen.dart 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/login_screen.dart b/Flutter_mobile/sample_todo_app/lib/screens/login_screen.dart new file mode 100644 index 0000000..3065e15 --- /dev/null +++ b/Flutter_mobile/sample_todo_app/lib/screens/login_screen.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:sample_todo_app/routing/go_router.dart'; + +class LoginScreen extends StatefulWidget { + const LoginScreen({super.key}); + + @override + State createState() => _LoginScreenState(); +} + +class _LoginScreenState extends State { + @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), + //padding: EdgeInsets.symmetric(horizontal: 15), + child: TextField( + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: 'Email', + hintText: 'Enter valid email like abc@gmail.com'), + ), + ), + const Padding( + padding: + EdgeInsets.only(left: 30.0, right: 30.0, top: 15, bottom: 0), + //padding: EdgeInsets.symmetric(horizontal: 15), + child: TextField( + obscureText: true, + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: 'Password', + hintText: 'Enter secure 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: () { + print('Successfully log in '); + context.pushNamed(AppRoute.homeScreen.name); + }, + child: const Text( + 'Log in ', + style: TextStyle(color: Colors.white, fontSize: 20), + ), + ), + ), + ), + ], + ), + ), + ); + } +} From 2f2f5e3906549dd5b18cc72f324b000665c44520 Mon Sep 17 00:00:00 2001 From: lttsNilesh Date: Thu, 30 Nov 2023 17:05:13 +0530 Subject: [PATCH 3/5] Updating the StatsigUser after successful login. --- Flutter_mobile/sample_todo_app/lib/main.dart | 3 +- .../lib/screens/login_screen.dart | 45 ++++++++++++++----- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/Flutter_mobile/sample_todo_app/lib/main.dart b/Flutter_mobile/sample_todo_app/lib/main.dart index 375d9fb..59b3ff5 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())); } diff --git a/Flutter_mobile/sample_todo_app/lib/screens/login_screen.dart b/Flutter_mobile/sample_todo_app/lib/screens/login_screen.dart index 3065e15..21f4e78 100644 --- a/Flutter_mobile/sample_todo_app/lib/screens/login_screen.dart +++ b/Flutter_mobile/sample_todo_app/lib/screens/login_screen.dart @@ -1,6 +1,9 @@ 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'; + +import '../api_key.dart'; class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); @@ -10,6 +13,22 @@ class LoginScreen extends StatefulWidget { } class _LoginScreenState extends State { + bool isLoading = false; + + void updateStatsigUser(BuildContext context) async { + setState(() { + isLoading = true; + }); + await Statsig.initialize( + statsigApiKey, 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( @@ -20,24 +39,22 @@ class _LoginScreenState extends State { const Padding( padding: EdgeInsets.only(left: 30.0, right: 30.0, top: 220, bottom: 0), - //padding: EdgeInsets.symmetric(horizontal: 15), child: TextField( decoration: InputDecoration( - border: OutlineInputBorder(), - labelText: 'Email', - hintText: 'Enter valid email like abc@gmail.com'), + border: OutlineInputBorder(), + labelText: 'Username', + ), ), ), const Padding( padding: EdgeInsets.only(left: 30.0, right: 30.0, top: 15, bottom: 0), - //padding: EdgeInsets.symmetric(horizontal: 15), child: TextField( obscureText: true, decoration: InputDecoration( - border: OutlineInputBorder(), - labelText: 'Password', - hintText: 'Enter secure password'), + border: OutlineInputBorder(), + labelText: 'Password', + ), ), ), SizedBox( @@ -53,8 +70,7 @@ class _LoginScreenState extends State { borderRadius: BorderRadius.circular(5), )), onPressed: () { - print('Successfully log in '); - context.pushNamed(AppRoute.homeScreen.name); + updateStatsigUser(context); }, child: const Text( 'Log in ', @@ -63,6 +79,15 @@ class _LoginScreenState extends State { ), ), ), + Visibility( + visible: isLoading, + child: const Padding( + padding: EdgeInsets.only(top: 30), + child: SizedBox( + height: 50, + width: 50, + child: CircularProgressIndicator(), + ))) ], ), ), From a4fb98f655dbfa02352cf1ba966c11baed7832f6 Mon Sep 17 00:00:00 2001 From: lttsNilesh Date: Thu, 30 Nov 2023 18:03:16 +0530 Subject: [PATCH 4/5] app lifecycle event in homescreen after login success. --- Flutter_mobile/sample_todo_app/lib/main.dart | 50 ------------------- .../lib/screens/home_screen.dart | 49 ++++++++++++++++++ 2 files changed, 49 insertions(+), 50 deletions(-) diff --git a/Flutter_mobile/sample_todo_app/lib/main.dart b/Flutter_mobile/sample_todo_app/lib/main.dart index 59b3ff5..ad38242 100644 --- a/Flutter_mobile/sample_todo_app/lib/main.dart +++ b/Flutter_mobile/sample_todo_app/lib/main.dart @@ -17,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/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], ); From ab930aca1d056f413b78f94a3952bfce179468b2 Mon Sep 17 00:00:00 2001 From: lttsNilesh Date: Thu, 30 Nov 2023 21:15:35 +0530 Subject: [PATCH 5/5] Updating user instead of re-initializing SDK --- Flutter_mobile/sample_todo_app/lib/screens/login_screen.dart | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Flutter_mobile/sample_todo_app/lib/screens/login_screen.dart b/Flutter_mobile/sample_todo_app/lib/screens/login_screen.dart index 21f4e78..40a673d 100644 --- a/Flutter_mobile/sample_todo_app/lib/screens/login_screen.dart +++ b/Flutter_mobile/sample_todo_app/lib/screens/login_screen.dart @@ -3,8 +3,6 @@ import 'package:go_router/go_router.dart'; import 'package:sample_todo_app/routing/go_router.dart'; import 'package:statsig/statsig.dart'; -import '../api_key.dart'; - class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); @@ -19,8 +17,7 @@ class _LoginScreenState extends State { setState(() { isLoading = true; }); - await Statsig.initialize( - statsigApiKey, StatsigUser(userId: "flutter_dummy_user_id")); + await Statsig.updateUser(StatsigUser(userId: "flutter_dummy_user_id")); Future.delayed(const Duration(milliseconds: 3000), () { setState(() { isLoading = false;