···109109110110### PDS Explorer (pdsls.dev replica)
111111112112-An in-app developer tool (debug builds) that replicates the core functionality
113113-of [pdsls.dev](https://pdsls.dev) — a client-side AT Protocol repository
114114-browser.
112112+An in-app developer tool accessible via Settings that replicates the core
113113+functionality of [pdsls.dev](https://pdsls.dev) — a client-side AT Protocol
114114+repository browser.
115115116116**Core features:**
117117···132132| `com.atproto.repo.listRecords` | Collection → paginated record list |
133133| `com.atproto.repo.getRecord` | Collection + rkey → full record JSON |
134134135135-Guard behind `kDebugMode`. No separate Bloc needed — use a `DevToolsCubit`
136136-with simple request/response state, since this is a stateless exploration tool.
135135+Accessible via Settings → Dev Tools. No separate Bloc needed — use a
136136+`DevToolsCubit` with simple request/response state, since this is a stateless
137137+exploration tool.
···2828- [ ] Collection browser via `listRecords` — paginated record list per collection
2929- [ ] Record inspector via `getRecord` — pretty-printed JSON with syntax highlighting
3030- [ ] AT-URI input — paste `at://` URI to jump directly to a record
3131-- [ ] Guard all dev tools screens behind `kDebugMode`
3131+- [ ] Add Dev Tools entry in Settings screen, navigable by all users
+1
ios/Flutter/Debug.xcconfig
···11+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
12#include "Generated.xcconfig"
+1
ios/Flutter/Release.xcconfig
···11+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
12#include "Generated.xcconfig"
+43
ios/Podfile
···11+# Uncomment this line to define a global platform for your project
22+# platform :ios, '13.0'
33+44+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
55+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
66+77+project 'Runner', {
88+ 'Debug' => :debug,
99+ 'Profile' => :release,
1010+ 'Release' => :release,
1111+}
1212+1313+def flutter_root
1414+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
1515+ unless File.exist?(generated_xcode_build_settings_path)
1616+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
1717+ end
1818+1919+ File.foreach(generated_xcode_build_settings_path) do |line|
2020+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
2121+ return matches[1].strip if matches
2222+ end
2323+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
2424+end
2525+2626+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
2727+2828+flutter_ios_podfile_setup
2929+3030+target 'Runner' do
3131+ use_frameworks!
3232+3333+ flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
3434+ target 'RunnerTests' do
3535+ inherit! :search_paths
3636+ end
3737+end
3838+3939+post_install do |installer|
4040+ installer.pods_project.targets.each do |target|
4141+ flutter_additional_ios_build_settings(target)
4242+ end
4343+end
···11import 'package:flutter/material.dart';
22-33-void main() {
44- runApp(const MyApp());
55-}
66-77-class MyApp extends StatelessWidget {
88- const MyApp({super.key});
99-1010- // This widget is the root of your application.
1111- @override
1212- Widget build(BuildContext context) {
1313- return MaterialApp(
1414- title: 'Flutter Demo',
1515- theme: ThemeData(
1616- // This is the theme of your application.
1717- //
1818- // TRY THIS: Try running your application with "flutter run". You'll see
1919- // the application has a purple toolbar. Then, without quitting the app,
2020- // try changing the seedColor in the colorScheme below to Colors.green
2121- // and then invoke "hot reload" (save your changes or press the "hot
2222- // reload" button in a Flutter-supported IDE, or press "r" if you used
2323- // the command line to start the app).
2424- //
2525- // Notice that the counter didn't reset back to zero; the application
2626- // state is not lost during the reload. To reset the state, use hot
2727- // restart instead.
2828- //
2929- // This works for code too, not just values: Most code changes can be
3030- // tested with just a hot reload.
3131- colorScheme: .fromSeed(seedColor: Colors.deepPurple),
3232- ),
3333- home: const MyHomePage(title: 'Flutter Demo Home Page'),
3434- );
3535- }
3636-}
22+import 'package:flutter_bloc/flutter_bloc.dart';
3733838-class MyHomePage extends StatefulWidget {
3939- const MyHomePage({super.key, required this.title});
44+import 'core/database/app_database.dart';
55+import 'core/router/app_router.dart';
66+import 'features/auth/bloc/auth_bloc.dart';
77+import 'features/auth/data/auth_repository.dart';
4084141- // This widget is the home page of your application. It is stateful, meaning
4242- // that it has a State object (defined below) that contains fields that affect
4343- // how it looks.
99+void main() async {
1010+ WidgetsFlutterBinding.ensureInitialized();
44114545- // This class is the configuration for the state. It holds the values (in this
4646- // case the title) provided by the parent (in this case the App widget) and
4747- // used by the build method of the State. Fields in a Widget subclass are
4848- // always marked "final".
1212+ final database = AppDatabase();
1313+ final authRepository = AuthRepository(database: database);
1414+ final authBloc = AuthBloc(authRepository: authRepository);
49155050- final String title;
1616+ authBloc.add(const CheckSessionRequested());
51175252- @override
5353- State<MyHomePage> createState() => _MyHomePageState();
1818+ runApp(LazuriteApp(authBloc: authBloc));
5419}
55205656-class _MyHomePageState extends State<MyHomePage> {
5757- int _counter = 0;
5858-5959- void _incrementCounter() {
6060- setState(() {
6161- // This call to setState tells the Flutter framework that something has
6262- // changed in this State, which causes it to rerun the build method below
6363- // so that the display can reflect the updated values. If we changed
6464- // _counter without calling setState(), then the build method would not be
6565- // called again, and so nothing would appear to happen.
6666- _counter++;
6767- });
6868- }
2121+class LazuriteApp extends StatelessWidget {
2222+ const LazuriteApp({super.key, required this.authBloc});
2323+ final AuthBloc authBloc;
69247025 @override
7126 Widget build(BuildContext context) {
7272- // This method is rerun every time setState is called, for instance as done
7373- // by the _incrementCounter method above.
7474- //
7575- // The Flutter framework has been optimized to make rerunning build methods
7676- // fast, so that you can just rebuild anything that needs updating rather
7777- // than having to individually change instances of widgets.
7878- return Scaffold(
7979- appBar: AppBar(
8080- // TRY THIS: Try changing the color here to a specific color (to
8181- // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
8282- // change color while the other colors stay the same.
8383- backgroundColor: Theme.of(context).colorScheme.inversePrimary,
8484- // Here we take the value from the MyHomePage object that was created by
8585- // the App.build method, and use it to set our appbar title.
8686- title: Text(widget.title),
8787- ),
8888- body: Center(
8989- // Center is a layout widget. It takes a single child and positions it
9090- // in the middle of the parent.
9191- child: Column(
9292- // Column is also a layout widget. It takes a list of children and
9393- // arranges them vertically. By default, it sizes itself to fit its
9494- // children horizontally, and tries to be as tall as its parent.
9595- //
9696- // Column has various properties to control how it sizes itself and
9797- // how it positions its children. Here we use mainAxisAlignment to
9898- // center the children vertically; the main axis here is the vertical
9999- // axis because Columns are vertical (the cross axis would be
100100- // horizontal).
101101- //
102102- // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
103103- // action in the IDE, or press "p" in the console), to see the
104104- // wireframe for each widget.
105105- mainAxisAlignment: .center,
106106- children: [
107107- const Text('You have pushed the button this many times:'),
108108- Text(
109109- '$_counter',
110110- style: Theme.of(context).textTheme.headlineMedium,
111111- ),
112112- ],
113113- ),
114114- ),
115115- floatingActionButton: FloatingActionButton(
116116- onPressed: _incrementCounter,
117117- tooltip: 'Increment',
118118- child: const Icon(Icons.add),
2727+ final router = AppRouter(authBloc: authBloc).router;
2828+2929+ return BlocProvider.value(
3030+ value: authBloc,
3131+ child: MaterialApp.router(
3232+ title: 'Lazurite',
3333+ debugShowCheckedModeBanner: false,
3434+ theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), useMaterial3: true),
3535+ routerConfig: router,
11936 ),
12037 );
12138 }
···11name: lazurite
22description: "A new Flutter project."
33-# The following line prevents the package from being accidentally published to
44-# pub.dev using `flutter pub publish`. This is preferred for private packages.
55-publish_to: "none" # Remove this line if you wish to publish to pub.dev
66-77-# The following defines the version and build number for your application.
88-# A version number is three numbers separated by dots, like 1.2.43
99-# followed by an optional build number separated by a +.
1010-# Both the version and the builder number may be overridden in flutter
1111-# build by specifying --build-name and --build-number, respectively.
1212-# In Android, build-name is used as versionName while build-number used as versionCode.
1313-# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
1414-# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
1515-# Read more about iOS versioning at
1616-# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
1717-# In Windows, build-name is used as the major, minor, and patch parts
1818-# of the product and file versions while build-number is used as the build suffix.
33+publish_to: "none"
194version: 1.0.0+1
205216environment:
···249dependencies:
2510 flutter:
2611 sdk: flutter
2727-2812 cupertino_icons: ^1.0.8
2913 bluesky: ^1.4.1
3014 bluesky_text: ^1.1.1
3115 atproto_oauth: ^0.2.0
3216 flutter_bloc: ^9.1.1
3333- drift: ^2.12.0
1717+ drift: ^2.24.0
1818+ drift_flutter: ^0.2.8
3419 go_router: ^17.1.0
2020+ path_provider: ^2.1.5
2121+ path: ^1.9.0
2222+ equatable: ^2.0.7
2323+ freezed_annotation: ^3.1.0
2424+ json_annotation: ^4.9.0
2525+ crypto: ^3.0.6
2626+ url_launcher: ^6.3.1
35273628dev_dependencies:
3729 flutter_test:
3830 sdk: flutter
3939-4040- # The "flutter_lints" package below contains a set of recommended lints to
4141- # encourage good coding practices. The lint set provided by the package is
4242- # activated in the `analysis_options.yaml` file located at the root of your
4343- # package. See that file for information about deactivating specific lint
4444- # rules and activating additional ones.
4531 flutter_lints: ^6.0.0
4646-4747-# For information on the generic Dart part of this file, see the
4848-# following page: https://dart.dev/tools/pub/pubspec
3232+ drift_dev: ^2.24.0
3333+ build_runner: ^2.4.15
3434+ freezed: ^3.0.0
3535+ json_serializable: ^6.8.0
3636+ bloc_test: ^10.0.0
3737+ mocktail: ^1.0.4
3838+ sqflite_common_ffi: ^2.3.5
49395050-# The following section is specific to Flutter packages.
5140flutter:
5241 uses-material-design: true
5353-5454- # To add assets to your application, add an assets section, like this:
5555- # assets:
5656- # - images/a_dot_burr.jpeg
5757- # - images/a_dot_ham.jpeg
5858-5959- # An image asset can refer to one or more resolution-specific "variants", see
6060- # https://flutter.dev/to/resolution-aware-images
6161-6262- # For details regarding adding assets from package dependencies, see
6363- # https://flutter.dev/to/asset-from-package
6464-6565- # To add custom fonts to your application, add a fonts section here,
6666- # in this "flutter" section. Each entry in this list should have a
6767- # "family" key with the font family name, and a "fonts" key with a
6868- # list giving the asset and other descriptors for the font. For
6969- # example:
7070- # fonts:
7171- # - family: Schyler
7272- # fonts:
7373- # - asset: fonts/Schyler-Regular.ttf
7474- # - asset: fonts/Schyler-Italic.ttf
7575- # style: italic
7676- # - family: Trajan Pro
7777- # fonts:
7878- # - asset: fonts/TrajanPro.ttf
7979- # - asset: fonts/TrajanPro_Bold.ttf
8080- # weight: 700
8181- #
8282- # For details regarding fonts from package dependencies,
8383- # see https://flutter.dev/to/font-from-package
···11-// This is a basic Flutter widget test.
22-//
33-// To perform an interaction with a widget in your test, use the WidgetTester
44-// utility in the flutter_test package. For example, you can send tap and scroll
55-// gestures. You can also use WidgetTester to find child widgets in the widget
66-// tree, read text, and verify that the values of widget properties are correct.
77-88-import 'package:flutter/material.dart';
99-import 'package:flutter_test/flutter_test.dart';
1010-1111-import 'package:lazurite/main.dart';
1212-1313-void main() {
1414- testWidgets('Counter increments smoke test', (WidgetTester tester) async {
1515- // Build our app and trigger a frame.
1616- await tester.pumpWidget(const MyApp());
1717-1818- // Verify that our counter starts at 0.
1919- expect(find.text('0'), findsOneWidget);
2020- expect(find.text('1'), findsNothing);
2121-2222- // Tap the '+' icon and trigger a frame.
2323- await tester.tap(find.byIcon(Icons.add));
2424- await tester.pump();
2525-2626- // Verify that our counter has incremented.
2727- expect(find.text('0'), findsNothing);
2828- expect(find.text('1'), findsOneWidget);
2929- });
3030-}