Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 1 addition & 52 deletions Flutter_mobile/sample_todo_app/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
}

Expand All @@ -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<ConsumerStatefulWidget> createState() {
return _DetectLifecycleState();
}
}

class _DetectLifecycleState extends ConsumerState<DetectLifecycle>
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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -12,14 +13,36 @@ class TodoRepository extends StateNotifier<List<Todo>> {
}

Future<void> 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<void> saveTodos(List<Todo> 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);
}

Expand Down Expand Up @@ -69,8 +92,7 @@ class TodoRepository extends StateNotifier<List<Todo>> {
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
];
}

Expand All @@ -86,7 +108,7 @@ class TodoRepository extends StateNotifier<List<Todo>> {
}

final todoRepositoryProvider =
StateNotifierProvider<TodoRepository, List<Todo>>((ref) {
StateNotifierProvider<TodoRepository, List<Todo>>((ref) {
return TodoRepository();
});

Expand Down
9 changes: 8 additions & 1 deletion Flutter_mobile/sample_todo_app/lib/routing/go_router.dart
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
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';

import '../models/todo.dart';
import '../screens/home_screen.dart';

enum AppRoute {
loginScreen,
homeScreen,
addTodoScreen,
tasksScreen,
}

final goRouterProvider = Provider<GoRouter>((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',
Expand Down
49 changes: 49 additions & 0 deletions Flutter_mobile/sample_todo_app/lib/screens/home_screen.dart
Original file line number Diff line number Diff line change
@@ -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<ConsumerStatefulWidget> createState() {
return _DetectLifecycleState();
}
}

class _DetectLifecycleState extends ConsumerState<DetectLifecycle>
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],
);
Expand Down
93 changes: 93 additions & 0 deletions Flutter_mobile/sample_todo_app/lib/screens/login_screen.dart
Original file line number Diff line number Diff line change
@@ -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<LoginScreen> createState() => _LoginScreenState();
}

class _LoginScreenState extends State<LoginScreen> {
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: <Widget>[
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(),
)))
],
),
),
);
}
}