feat: starter kit.

This commit is contained in:
Darshan
2025-02-07 17:53:36 +05:30
parent 4705d9ba34
commit 1f9a482d56
208 changed files with 7497 additions and 2 deletions

View File

@@ -0,0 +1,92 @@
import 'dart:math';
import 'package:appwrite_flutter_starter_kit/utils/extensions/colors.dart';
import 'package:flutter/material.dart';
/// Background color for gradients, blur, etc.
const Color checkeredBackgroundColor = Color(0xFFFAFAFB);
/// Max height factor for background.
const double heightConstraintFactor = 0.5;
/// A custom widget that applies a checkered background pattern with gradient effects.
class CheckeredBackground extends StatelessWidget {
final Widget child;
const CheckeredBackground({super.key, required this.child});
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: CheckeredBackgroundPainter(),
child: child,
);
}
}
/// A custom painter that draws a checkered background pattern with vertical and horizontal grid lines,
/// along with linear and radial gradient overlays for visual enhancement.
class CheckeredBackgroundPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final double gridSize = min(size.width * 0.1, 64);
final double lineThickness = 0.75;
final double heightConstraint = size.height * heightConstraintFactor;
final Paint linePaint = Paint()
..color = Colors.grey.applyOpacity(0.3)
..strokeWidth = lineThickness;
// Draw vertical lines
for (double x = 0; x <= size.width; x += gridSize) {
canvas.drawLine(Offset(x, 0), Offset(x, heightConstraint), linePaint);
}
// Draw horizontal lines
for (double y = 0; y <= heightConstraint; y += gridSize) {
canvas.drawLine(Offset(0, y), Offset(size.width, y), linePaint);
}
// Apply gradient overlays
_drawRadialGradientOverlay(canvas, size);
_drawLinearGradientOverlay(canvas, size, heightConstraint);
}
/// Draws a radial gradient overlay over the canvas to create a smooth blend effect.
void _drawRadialGradientOverlay(Canvas canvas, Size size) {
final Paint paint = Paint()
..shader = RadialGradient(
colors: [
checkeredBackgroundColor.applyOpacity(0.0),
checkeredBackgroundColor.applyOpacity(0.4),
checkeredBackgroundColor.applyOpacity(0.2),
Colors.transparent,
],
center: Alignment.center,
radius: 2.0,
).createShader(Rect.fromLTWH(0, 0, size.width, size.height));
canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), paint);
}
/// Draws a vertical gradient overlay over the canvas to enhance the checkered background's appearance.
void _drawLinearGradientOverlay(
Canvas canvas, Size size, double heightConstraint) {
final Paint paint = Paint()
..shader = LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
checkeredBackgroundColor,
checkeredBackgroundColor.applyOpacity(0.3),
checkeredBackgroundColor.applyOpacity(0.5),
checkeredBackgroundColor.applyOpacity(0.7),
checkeredBackgroundColor,
],
).createShader(Rect.fromLTWH(0, 0, size.width, heightConstraint));
canvas.drawRect(Rect.fromLTWH(0, 0, size.width, heightConstraint), paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

View File

@@ -0,0 +1,517 @@
import 'package:appwrite_flutter_starter_kit/data/models/log.dart';
import 'package:appwrite_flutter_starter_kit/data/models/project_info.dart';
import 'package:appwrite_flutter_starter_kit/ui/components/responsive_layout.dart';
import 'package:appwrite_flutter_starter_kit/ui/components/single_wrap.dart';
import 'package:appwrite_flutter_starter_kit/utils/extensions/colors.dart';
import 'package:flutter/material.dart';
class CollapsibleBottomSheet extends StatefulWidget {
final String title;
final List<Log> logs;
final ProjectInfo projectInfo;
const CollapsibleBottomSheet({
super.key,
this.title = "Logs",
required this.logs,
required this.projectInfo,
});
@override
State<CollapsibleBottomSheet> createState() => _CollapsibleBottomSheetState();
}
class _CollapsibleBottomSheetState extends State<CollapsibleBottomSheet> {
bool isExpanded = false;
@override
Widget build(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
decoration: const BoxDecoration(
color: Colors.white,
border: Border(
top: BorderSide(color: Color(0xFFEDEDF0), width: 1),
),
),
child: SingleWrap(
child: Column(
children: [
SizedBox(
width: double.infinity,
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () => setState(() => isExpanded = !isExpanded),
behavior: HitTestBehavior.opaque,
// Captures taps on empty spaces too
child: Container(
padding: const EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Title & Logs Count
Row(
children: [
Text(
widget.title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF56565C),
),
),
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 2,
),
decoration: BoxDecoration(
color: Colors.black.applyOpacity(0.1),
borderRadius: BorderRadius.circular(6),
),
child: Text(
widget.logs.length.toString(),
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF56565C),
),
),
),
],
),
// Expand/Collapse Icon
AnimatedRotation(
turns: isExpanded ? 0.5 : 0.0,
duration: const Duration(milliseconds: 250),
child: const Icon(
Icons.keyboard_arrow_down,
color: Color(0xFF97979B),
),
),
],
),
),
),
),
),
AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
height: isExpanded
? MediaQuery.of(context).size.height * 0.575
: 0, // Expandable height
child: isExpanded
? SingleChildScrollView(
child: LogsBottomSheet(
logs: widget.logs,
projectInfo: widget.projectInfo,
),
)
: null,
),
],
),
),
);
}
}
class LogsBottomSheet extends StatelessWidget {
final List<Log> logs;
final ProjectInfo projectInfo;
const LogsBottomSheet({
super.key,
required this.logs,
required this.projectInfo,
});
@override
Widget build(BuildContext context) {
return ResponsiveLayout(
smallDeviceLayout: Column(
children: [
// Project Info Section
ProjectSection(projectInfo: projectInfo),
// Logs Table
logs.isEmpty
? const EmptyLogsSection()
: Column(
children: [
LogsTableHeader(),
ListView.builder(
shrinkWrap: true,
itemCount: logs.length,
itemBuilder: (context, index) {
return LogsTableRow(
log: logs[index],
needsScroll: true,
);
},
),
],
),
],
),
largeDeviceLayout: SizedBox(
height: MediaQuery.of(context).size.height,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 1,
child: ProjectSection(projectInfo: projectInfo),
),
VerticalDivider(
width: 1,
thickness: 1,
color: Color(0xFFEDEDF0),
),
Expanded(
flex: 2,
child: logs.isEmpty
? EmptyLogsSection()
: Column(
children: [
LogsTableHeader(),
Expanded(
child: ListView.builder(
itemCount: logs.length,
itemBuilder: (context, index) => LogsTableRow(
log: logs[index],
),
),
),
],
),
),
],
),
));
}
}
class ProjectSection extends StatelessWidget {
final ProjectInfo projectInfo;
const ProjectSection({
super.key,
required this.projectInfo,
});
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header with Background & Dividers
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
decoration: const BoxDecoration(
color: Color(0xFFFAFAFB),
border: Border.symmetric(
horizontal: BorderSide(color: Color(0xFFEDEDF0), width: 1),
),
),
child: const Text(
"Project",
style: TextStyle(
fontSize: 14,
color: Color(0xFF97979B),
fontWeight: FontWeight.w500,
),
),
),
// Grid Layout for Project Info
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ProjectRow(title: "Endpoint", value: projectInfo.endpoint),
ProjectRow(title: "Project ID", value: projectInfo.projectId),
],
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ProjectRow(
title: "Project Name", value: projectInfo.projectName),
ProjectRow(title: "Version", value: projectInfo.version),
],
),
],
),
),
],
);
}
}
/// **Reusable Row for Project Details**
class ProjectRow extends StatelessWidget {
final String title;
final String value;
const ProjectRow({super.key, required this.title, required this.value});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 12,
color: Color(0xFF97979B),
),
),
const SizedBox(height: 4),
Text(
value,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 14,
color: Color(0xFF56565C),
fontWeight: FontWeight.w500,
),
),
],
);
}
}
class EmptyLogsSection extends StatelessWidget {
const EmptyLogsSection({super.key});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header with Background & Dividers
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
decoration: const BoxDecoration(
color: Color(0xFFFAFAFB),
border: Border.symmetric(
horizontal: BorderSide(color: Color(0xFFEDEDF0), width: 1),
),
),
child: const Text(
"Logs",
style: TextStyle(
fontSize: 14,
color: Color(0xFF97979B),
fontWeight: FontWeight.w500,
),
),
),
// Empty State Message
const Padding(
padding: EdgeInsets.all(16),
child: Text(
"There are no logs to show",
style: TextStyle(
fontSize: 14,
color: Color(0xFF56565C),
fontFamily: 'monospace',
),
),
),
],
);
}
}
class LogsTableHeader extends StatelessWidget {
const LogsTableHeader({super.key});
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
decoration: const BoxDecoration(
color: Color(0xFFFAFAFB),
border: Border.symmetric(
horizontal: BorderSide(color: Color(0xFFEDEDF0), width: 1),
),
),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: _columns.map((column) {
return SizedBox(
width: column.$2, // Fixed width per column
child: Text(
column.$1,
style: const TextStyle(
fontSize: 14,
color: Color(0xFF97979B),
fontWeight: FontWeight.w500,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
);
}).toList(),
),
),
);
}
}
// Column definitions (Equivalent to the `columns` list in Compose)
const List<(String, double)> _columns = [
("Date", 150),
("Status", 80),
("Method", 100),
("Path", 125),
("Response", 300),
];
class LogsTableRow extends StatelessWidget {
final Log log;
final bool needsScroll;
const LogsTableRow({
super.key,
required this.log,
this.needsScroll = false,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(top: 16, left: 16, bottom: 8),
child: needsScroll
? SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
_buildTextCell(log.date, _columns[0].$2, monospace: true),
_buildStatusCell(log.status.toString(), _columns[1].$2),
_buildTextCell(log.method, _columns[2].$2,
monospace: true),
_buildTextCell(log.path, _columns[3].$2, monospace: true),
_buildResponseCell(log.response, _columns[4].$2),
],
),
)
: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
_buildTextCell(log.date, _columns[0].$2, monospace: true),
_buildStatusCell(log.status.toString(), _columns[1].$2),
_buildTextCell(log.method, _columns[2].$2, monospace: true),
_buildTextCell(log.path, _columns[3].$2, monospace: true),
Expanded(
child:
_buildResponseCell(log.response, _columns[4].$2)),
],
),
),
const Divider(color: Color(0xFFEDEDF0), thickness: 1),
],
);
}
/// Builds a standard text cell.
Widget _buildTextCell(String text, double width, {bool monospace = false}) {
return SizedBox(
width: width,
child: Text(
text,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14,
fontFamily: monospace ? 'monospace' : null,
color: const Color(0xFF56565C),
),
),
);
}
/// Builds the status cell with dynamic colors.
Widget _buildStatusCell(String status, double width) {
final isSuccess = int.tryParse(status) != null &&
(200 <= int.parse(status) && int.parse(status) <= 399);
final color = isSuccess ? const Color(0xFF0A714F) : const Color(0xFFB31212);
final bgColor =
isSuccess ? const Color(0x4010B981) : const Color(0x40FF453A);
return SizedBox(
width: width,
child: SingleWrap(
child: Container(
padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(6),
),
child: Text(
status,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: color,
),
),
),
),
);
}
/// Builds the response cell with a gray background.
Widget _buildResponseCell(String response, double width) {
return SingleWrap(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 2),
decoration: BoxDecoration(
color: Colors.grey.applyOpacity(0.25),
borderRadius: BorderRadius.circular(3),
),
child: Tooltip(
message: response.replaceAll(". ", ".\n"),
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: Text(
response.substring(0, 50),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 12,
color: Color(0xFF56565C),
fontFamily: 'monospace',
),
),
),
),
),
);
}
}

View File

@@ -0,0 +1,109 @@
import 'package:appwrite_flutter_starter_kit/utils/extensions/build_context.dart';
import 'package:flutter/material.dart';
/// A widget that animates a connection line with a checkmark in the middle.
/// The left and right lines expand and contract based on the `show` state, with a tick appearing after a delay.
///
/// [show] - Controls whether the connection line animation and the tick are visible.
class ConnectionLine extends StatelessWidget {
final bool show;
const ConnectionLine({super.key, required this.show});
@override
Widget build(BuildContext context) {
return SizedBox(
width: context.widthFactor(
mobileFactor: 0.25,
largeScreenFactor: 0.15,
),
child: Flex(
direction: Axis.horizontal,
children: [
// Left sideline with smooth expansion
AnimatedSideline(left: true, show: show),
// Animated checkmark
AnimatedCheckmark(show: show),
// Right sideline with smooth expansion
AnimatedSideline(left: false, show: show),
],
),
);
}
}
/// A widget that animates horizontal sidelines with a gradient effect.
/// Handles both visibility and width expansion smoothly.
///
/// [left] - Indicates whether the sideline is on the left side (true) or right side (false).
/// [show] - Controls the visibility and animation of the sideline.
class AnimatedSideline extends StatelessWidget {
final bool left;
final bool show;
const AnimatedSideline({super.key, required this.left, required this.show});
@override
Widget build(BuildContext context) {
return Expanded(
child: LayoutBuilder(builder: (context, constraints) {
return Align(
alignment: !left ? Alignment.centerLeft : Alignment.centerRight,
child: AnimatedContainer(
duration: Duration(milliseconds: 750),
width: show ? constraints.maxWidth : 0,
child: SizedBox(
height: 1.5,
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: left
? [
const Color(0x26FE9567),
const Color(0xFFF02E65),
]
: [
const Color(0xFFF02E65),
const Color(0x26FE9567),
],
),
),
),
),
),
);
}),
);
}
}
/// A widget that animates the checkmark icon's appearance with a fade-in effect.
///
/// [show] - Determines when the checkmark appears.
class AnimatedCheckmark extends StatelessWidget {
final bool show;
const AnimatedCheckmark({super.key, required this.show});
@override
Widget build(BuildContext context) {
return AnimatedOpacity(
opacity: show ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: Container(
width: 30,
height: 30,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: const Color(0x14F02E65),
border: Border.all(color: const Color(0x80F02E65), width: 1.8),
),
child: const Center(
child: Icon(Icons.check, size: 15, color: Color(0xFFFD366E)),
),
),
);
}
}

View File

@@ -0,0 +1,175 @@
import 'package:appwrite_flutter_starter_kit/data/models/status.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
/// A widget that displays the current connection status and allows the user to send a ping.
class ConnectionStatusView extends StatelessWidget {
final Status status;
final VoidCallback onButtonClick;
const ConnectionStatusView({
super.key,
required this.status,
required this.onButtonClick,
});
/// Simulates sending a ping with a loading state.
Future<void> _sendPing() async {
HapticFeedback.mediumImpact();
onButtonClick();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 80,
child: AnimatedSwitcher(
duration: kThemeAnimationDuration,
child: _buildStatusContent(),
),
),
const SizedBox(height: 24),
AnimatedOpacity(
duration: const Duration(milliseconds: 300),
opacity: status == Status.loading ? 0.0 : 1.0,
child: ElevatedButton(
onPressed: status == Status.loading ? null : _sendPing,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFFD366E),
disabledBackgroundColor: const Color(0xFFEDEDF0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
minimumSize: const Size(0, 32),
elevation: 0,
),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 10,
horizontal: 6,
),
child: Text(
"Send a ping",
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.white,
letterSpacing: 0.1,
),
),
),
),
),
],
),
);
}
/// Builds the status message UI dynamically.
Widget _buildStatusContent() {
return Align(
key: ValueKey(status),
alignment: Alignment.center,
child: switch (status) {
Status.loading => _buildLoadingIndicator(),
Status.success => _buildSuccessMessage(),
Status.error => _buildIdleMessage(),
Status.idle || Status.error => _buildIdleMessage(),
},
);
}
/// Loading State
Widget _buildLoadingIndicator() {
return Row(
key: const ValueKey(Status.loading),
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.grey,
),
),
const SizedBox(width: 16),
const Text(
"Waiting for connection...",
style: TextStyle(
fontSize: 14,
height: 19.6 / 14,
fontWeight: FontWeight.w400,
color: Color(0xFF2D2D31),
letterSpacing: 0.1,
),
),
],
);
}
/// Success State
Widget _buildSuccessMessage() {
return Column(
key: const ValueKey(Status.success),
children: const [
Text(
"Congratulations!",
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w400,
color: Color(0xFF2D2D31),
),
),
SizedBox(height: 8),
Text(
"You connected your app successfully.",
style: TextStyle(
fontSize: 14,
height: 19.6 / 14,
fontWeight: FontWeight.w400,
color: Color(0xFF56565C),
letterSpacing: 0.1,
),
textAlign: TextAlign.center,
),
],
);
}
/// Idle State
Widget _buildIdleMessage() {
return Column(
key: const ValueKey(Status.idle),
children: const [
Text(
"Check connection",
style: TextStyle(
fontSize: 24,
height: 28.8 / 24,
fontWeight: FontWeight.w400,
color: Color(0xFF2D2D31),
),
),
SizedBox(height: 8),
Text(
"Send a ping to verify the connection.",
style: TextStyle(
fontSize: 14,
height: 19.6 / 14,
fontWeight: FontWeight.w400,
color: Color(0xFF56565C),
letterSpacing: 0.1,
),
textAlign: TextAlign.center,
),
],
);
}
}

View File

@@ -0,0 +1,155 @@
import 'package:appwrite_flutter_starter_kit/utils/extensions/build_context.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
/// A widget that contains a list of informational cards displayed vertically.
class GettingStartedCards extends StatelessWidget {
const GettingStartedCards({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
GeneralInfoCard(
title: "Edit your app",
link: null,
subtitle: const HighlightedText(),
),
GeneralInfoCard(
title: "Head to Appwrite Cloud",
link: "https://cloud.appwrite.io",
subtitle: const Text(
"Start managing your project from the Appwrite console",
style: TextStyle(
fontSize: 14,
color: Color(0xFF56565C),
),
),
),
GeneralInfoCard(
title: "Explore docs",
link: "https://appwrite.io/docs",
subtitle: const Text(
"Discover the full power of Appwrite by diving into our documentation",
style: TextStyle(
fontSize: 14,
color: Color(0xFF56565C),
),
),
),
],
),
);
}
}
/// A reusable card component that displays a title and subtitle with optional link functionality.
class GeneralInfoCard extends StatelessWidget {
final String title;
final String? link;
final Widget subtitle;
const GeneralInfoCard({
super.key,
required this.title,
this.link,
required this.subtitle,
});
void _openLink() async {
if (link != null && await canLaunchUrl(Uri.parse(link!))) {
await launchUrl(Uri.parse(link!));
}
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
return SizedBox(
// `1` because we already have padding on sides.
width: constraints.maxWidth * (context.isExtraWideScreen ? 0.55 : 1),
child: Card(
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: const BorderSide(color: Color(0xFFEDEDF0), width: 1),
),
color: Colors.white,
child: InkWell(
onTap: link != null ? _openLink : null,
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
title,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w400,
height: 26 / 20,
),
),
if (link != null) ...[
const Spacer(),
const Icon(
Icons.arrow_forward,
size: 18,
color: Color(0xFFD8D8DB),
),
]
],
),
const SizedBox(height: 8),
subtitle,
],
),
),
),
),
);
});
}
}
/// A widget that displays highlighted text with a specific word or phrase styled differently.
class HighlightedText extends StatelessWidget {
const HighlightedText({super.key});
@override
Widget build(BuildContext context) {
return RichText(
text: TextSpan(
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Color(0xFF56565C),
),
children: [
const TextSpan(text: "Edit "),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Container(
decoration: BoxDecoration(
color: const Color(0xFFEDEDF0),
borderRadius: BorderRadius.circular(4),
),
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
child: const Text(
" lib/main.dart ",
style: TextStyle(color: Colors.black),
),
),
),
const TextSpan(text: " to get started with building your app"),
],
),
);
}
}

View File

@@ -0,0 +1,38 @@
import 'package:flutter/material.dart';
/// A widget that provides a responsive layout by switching between two layouts
/// based on the available screen width.
///
/// This widget uses a [LayoutBuilder] to determine if the screen width exceeds
/// the given [breakPoint]. If it does, the [largeDeviceLayout] is displayed;
/// otherwise, the [smallDeviceLayout] is shown.
///
/// Example usage:
/// ```dart
/// ResponsiveLayout(
/// breakPoint: 768,
/// smallDeviceLayout: SmallScreenWidget(),
/// largeDeviceLayout: LargeScreenWidget(),
/// )
/// ```
class ResponsiveLayout extends StatelessWidget {
final int breakPoint;
final Widget smallDeviceLayout;
final Widget largeDeviceLayout;
/// Creates a [ResponsiveLayout] widget.
const ResponsiveLayout({
super.key,
this.breakPoint = 1024,
required this.smallDeviceLayout,
required this.largeDeviceLayout,
});
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
bool isRunningOnALargeDevice = constraints.maxWidth > breakPoint;
return isRunningOnALargeDevice ? largeDeviceLayout : smallDeviceLayout;
});
}
}

View File

@@ -0,0 +1,14 @@
import 'package:flutter/material.dart';
class SingleWrap extends StatelessWidget {
final Widget child;
const SingleWrap({super.key, required this.child});
@override
Widget build(BuildContext context) {
return Wrap(
children: [child],
);
}
}

View File

@@ -0,0 +1,90 @@
import 'package:appwrite_flutter_starter_kit/data/models/status.dart';
import 'package:appwrite_flutter_starter_kit/ui/icons/appwrite.dart';
import 'package:flutter/material.dart';
import 'connection_line.dart';
/// A widget that displays a row containing platform icons and a connection line between them.
/// The connection line indicates the status of the platform connection.
///
/// [status] - A boolean indicating whether the connection is successful.
class TopPlatformView extends StatelessWidget {
final Status status;
const TopPlatformView({
super.key,
required this.status,
});
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
PlatformIcon(child: FlutterLogo(size: 40)),
ConnectionLine(show: status == Status.success),
PlatformIcon(child: AppwriteIcon(size: 40)),
],
);
}
}
/// A widget that displays a stylized platform icon with a customizable content block.
/// The icon is rendered with shadows, rounded corners, and a layered background.
///
/// [size] - The size of the platform icon.
/// [child] - The inner content of the icon (e.g., an image or icon).
class PlatformIcon extends StatelessWidget {
final double size;
final Widget child;
const PlatformIcon({
super.key,
this.size = 100.0,
required this.child,
});
@override
Widget build(BuildContext context) {
return Container(
width: size,
height: size,
decoration: BoxDecoration(
color: const Color(0xFFFAFAFD),
borderRadius: BorderRadius.circular(24),
border: Border.all(color: const Color(0x0A19191C), width: 1),
boxShadow: [
BoxShadow(
color: const Color(0x08000000),
blurRadius: 9.36,
offset: const Offset(0, 4),
),
],
),
child: Center(
child: Container(
width: size * 0.86,
height: size * 0.86,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFFAFAFB), width: 1),
boxShadow: [
BoxShadow(
color: const Color(0x05000000),
blurRadius: 8,
offset: const Offset(0, 2),
),
BoxShadow(
color: const Color(0x05000000),
blurRadius: 12,
offset: const Offset(0, 3),
),
],
),
child: Center(child: child),
),
),
);
}
}