worked on profile-module

This commit is contained in:
Vinayakkadge04
2025-10-14 17:31:59 +05:30
parent 4264ddd623
commit 77e677b4f8
18 changed files with 2037 additions and 7 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
assets/icons/contact_us.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
assets/icons/faq.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
assets/icons/privacy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 703 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,858 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// @docImport 'circle_avatar.dart';
/// @docImport 'text_theme.dart';
library;
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
const Duration _kExpand = Duration(milliseconds: 200);
/// Enables control over a single [ExpansionTile]'s expanded/collapsed state.
///
/// It can be useful to expand or collapse an [ExpansionTile]
/// programmatically, for example to reconfigure an existing expansion
/// tile based on a system event. To do so, create an [ExpansionTile]
/// with an [ExpansionTileController] that's owned by a stateful widget
/// or look up the tile's automatically created [ExpansionTileController]
/// with [ExpansibleController.of].
///
/// {@tool dartpad}
/// Typical usage of the [ExpansibleController.of] function is to call it from within the
/// `build` method of a descendant of an [ExpansionTile].
///
/// When the [ExpansionTile] is actually created in the same `build`
/// function as the callback that refers to the controller, then the
/// `context` argument to the `build` function can't be used to find
/// the [ExpansionTileController] (since it's "above" the widget
/// being returned in the widget tree). In cases like that you can
/// add a [Builder] widget, which provides a new scope with a
/// [BuildContext] that is "under" the [ExpansionTile]:
///
/// ** See code in examples/api/lib/material/expansion_tile/expansion_tile.1.dart **
/// {@end-tool}
///
/// A more efficient solution is to split your build function into
/// several widgets. This introduces a new context from which you
/// can obtain the [ExpansionTileController]. With this approach you
/// would have an outer widget that creates the [ExpansionTile]
/// populated by instances of your new inner widgets, and then in
/// these inner widgets you would use `ExpansionTileController.of`.
///
/// The [ExpansibleController.expand] and [ExpansibleController.collapse]
/// methods cause the [ExpansionTile] to rebuild, so they may not be called from
/// a build method.
///
/// Remember to dispose of the [ExpansionTileController] when it is no longer
/// needed. This will ensure we discard any resources used by the object.
@Deprecated(
'Use ExpansibleController instead. '
'This feature was deprecated after v3.31.0-0.1.pre.',
)
typedef ExpansionTileController = ExpansibleController;
/// A single-line [ListTile] with an expansion arrow icon that expands or collapses
/// the tile to reveal or hide the [children].
///
/// This widget is typically used with [ListView] to create an "expand /
/// collapse" list entry. When used with scrolling widgets like [ListView], a
/// unique [PageStorageKey] must be specified as the [key], to enable the
/// [ExpansionTile] to save and restore its expanded state when it is scrolled
/// in and out of view.
///
/// This class overrides the [ListTileThemeData.iconColor] and [ListTileThemeData.textColor]
/// theme properties for its [ListTile]. These colors animate between values when
/// the tile is expanded and collapsed: between [iconColor], [collapsedIconColor] and
/// between [textColor] and [collapsedTextColor].
///
/// The expansion arrow icon is shown on the right by default in left-to-right languages
/// (i.e. the trailing edge). This can be changed using [controlAffinity]. This maps
/// to the [leading] and [trailing] properties of [ExpansionTile].
///
/// {@tool dartpad}
/// This example demonstrates how the [ExpansionTile] icon's location and appearance
/// can be customized.
///
/// ** See code in examples/api/lib/material/expansion_tile/expansion_tile.0.dart **
/// {@end-tool}
///
/// {@tool dartpad}
/// This example demonstrates how an [ExpansionTileController] can be used to
/// programmatically expand or collapse an [ExpansionTile].
///
/// ** See code in examples/api/lib/material/expansion_tile/expansion_tile.1.dart **
/// {@end-tool}
///
/// See also:
///
/// * [ListTile], useful for creating expansion tile [children] when the
/// expansion tile represents a sublist.
/// * The "Expand and collapse" section of
/// <https://material.io/components/lists#types>
class CustomExpansionTile extends StatefulWidget {
/// Creates a single-line [ListTile] with an expansion arrow icon that expands or collapses
/// the tile to reveal or hide the [children]. The [initiallyExpanded] property must
/// be non-null.
const CustomExpansionTile({
super.key,
this.leading,
required this.title,
this.subtitle,
this.onExpansionChanged,
this.children = const <Widget>[],
this.trailing,
this.showTrailingIcon = true,
this.initiallyExpanded = false,
this.maintainState = false,
this.tilePadding,
this.expandedCrossAxisAlignment,
this.expandedAlignment,
this.childrenPadding,
this.backgroundColor,
this.collapsedBackgroundColor,
this.textColor,
this.collapsedTextColor,
this.iconColor,
this.collapsedIconColor,
this.shape,
this.collapsedShape,
this.clipBehavior,
this.controller,
this.dense,
this.visualDensity,
this.minTileHeight,
this.enableFeedback = true,
this.enabled = true,
this.expansionAnimationStyle,
this.internalAddSemanticForOnTap = false,
this.borderRadius,
this.controlAffinity,
}) : assert(
expandedCrossAxisAlignment != CrossAxisAlignment.baseline,
'CrossAxisAlignment.baseline is not supported since the expanded children '
'are aligned in a column, not a row. Try to use another constant.',
);
/// A widget to display before the title.
///
/// Typically a [CircleAvatar] widget.
///
/// Depending on the value of [controlAffinity], the [leading] widget
/// may replace the rotating expansion arrow icon.
final Widget? leading;
/// The primary content of the list item.
///
/// Typically a [Text] widget.
final Widget title;
/// Additional content displayed below the title.
///
/// Typically a [Text] widget.
final Widget? subtitle;
/// Called when the tile expands or collapses.
///
/// When the tile starts expanding, this function is called with the value
/// true. When the tile starts collapsing, this function is called with
/// the value false.
///
/// Instead of providing this property, consider adding this callback as a
/// listener to a provided [controller].
final ValueChanged<bool>? onExpansionChanged;
/// The widgets that are displayed when the tile expands.
///
/// Typically [ListTile] widgets.
final List<Widget> children;
/// The color to display behind the sublist when expanded.
///
/// If this property is null then [ExpansionTileThemeData.backgroundColor] is used. If that
/// is also null then Colors.transparent is used.
///
/// See also:
///
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
/// [ExpansionTileThemeData].
final Color? backgroundColor;
/// When not null, defines the background color of tile when the sublist is collapsed.
///
/// If this property is null then [ExpansionTileThemeData.collapsedBackgroundColor] is used.
/// If that is also null then Colors.transparent is used.
///
/// See also:
///
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
/// [ExpansionTileThemeData].
final Color? collapsedBackgroundColor;
/// A widget to display after the title.
///
/// Depending on the value of [controlAffinity], the [trailing] widget
/// may replace the rotating expansion arrow icon.
final Widget? trailing;
/// Specifies if the [ExpansionTile] should build a default trailing icon if [trailing] is null.
final bool showTrailingIcon;
/// Specifies if the list tile is initially expanded (true) or collapsed (false).
///
/// Alternatively, a provided [controller] can be used to initially expand the
/// tile if [ExpansibleController.expand] is called before this widget is built.
///
/// Defaults to false.
final bool initiallyExpanded;
/// Specifies whether the state of the children is maintained when the tile expands and collapses.
///
/// When true, the children are kept in the tree while the tile is collapsed.
/// When false (default), the children are removed from the tree when the tile is
/// collapsed and recreated upon expansion.
final bool maintainState;
/// Specifies padding for the [ListTile].
///
/// Analogous to [ListTile.contentPadding], this property defines the insets for
/// the [leading], [title], [subtitle] and [trailing] widgets. It does not inset
/// the expanded [children] widgets.
///
/// If this property is null then [ExpansionTileThemeData.tilePadding] is used. If that
/// is also null then the tile's padding is `EdgeInsets.symmetric(horizontal: 16.0)`.
///
/// See also:
///
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
/// [ExpansionTileThemeData].
final EdgeInsetsGeometry? tilePadding;
/// Specifies the alignment of [children], which are arranged in a column when
/// the tile is expanded.
///
/// The internals of the expanded tile make use of a [Column] widget for
/// [children], and [Align] widget to align the column. The [expandedAlignment]
/// parameter is passed directly into the [Align].
///
/// Modifying this property controls the alignment of the column within the
/// expanded tile, not the alignment of [children] widgets within the column.
/// To align each child within [children], see [expandedCrossAxisAlignment].
///
/// The width of the column is the width of the widest child widget in [children].
///
/// If this property is null then [ExpansionTileThemeData.expandedAlignment]is used. If that
/// is also null then the value of [expandedAlignment] is [Alignment.center].
///
/// See also:
///
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
/// [ExpansionTileThemeData].
final Alignment? expandedAlignment;
/// Specifies the alignment of each child within [children] when the tile is expanded.
///
/// The internals of the expanded tile make use of a [Column] widget for
/// [children], and the `crossAxisAlignment` parameter is passed directly into
/// the [Column].
///
/// Modifying this property controls the cross axis alignment of each child
/// within its [Column]. The width of the [Column] that houses [children] will
/// be the same as the widest child widget in [children]. The width of the
/// [Column] might not be equal to the width of the expanded tile.
///
/// To align the [Column] along the expanded tile, use the [expandedAlignment]
/// property instead.
///
/// When the value is null, the value of [expandedCrossAxisAlignment] is
/// [CrossAxisAlignment.center].
final CrossAxisAlignment? expandedCrossAxisAlignment;
/// Specifies padding for [children].
///
/// If this property is null then [ExpansionTileThemeData.childrenPadding] is used. If that
/// is also null then the value of [childrenPadding] is [EdgeInsets.zero].
///
/// See also:
///
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
/// [ExpansionTileThemeData].
final EdgeInsetsGeometry? childrenPadding;
/// The icon color of tile's expansion arrow icon when the sublist is expanded.
///
/// Used to override to the [ListTileThemeData.iconColor].
///
/// If this property is null then [ExpansionTileThemeData.iconColor] is used. If that
/// is also null then the value of [ColorScheme.primary] is used.
///
/// See also:
///
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
/// [ExpansionTileThemeData].
final Color? iconColor;
/// The icon color of tile's expansion arrow icon when the sublist is collapsed.
///
/// Used to override to the [ListTileThemeData.iconColor].
///
/// If this property is null then [ExpansionTileThemeData.collapsedIconColor] is used. If that
/// is also null and [ThemeData.useMaterial3] is true, [ColorScheme.onSurface] is used. Otherwise,
/// defaults to [ThemeData.unselectedWidgetColor] color.
///
/// See also:
///
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
/// [ExpansionTileThemeData].
final Color? collapsedIconColor;
/// The color of the tile's titles when the sublist is expanded.
///
/// Used to override to the [ListTileThemeData.textColor].
///
/// If this property is null then [ExpansionTileThemeData.textColor] is used. If that
/// is also null then and [ThemeData.useMaterial3] is true, color of the [TextTheme.bodyLarge]
/// will be used for the [title] and [subtitle]. Otherwise, defaults to [ColorScheme.primary] color.
///
/// See also:
///
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
/// [ExpansionTileThemeData].
final Color? textColor;
/// The color of the tile's titles when the sublist is collapsed.
///
/// Used to override to the [ListTileThemeData.textColor].
///
/// If this property is null then [ExpansionTileThemeData.collapsedTextColor] is used.
/// If that is also null and [ThemeData.useMaterial3] is true, color of the
/// [TextTheme.bodyLarge] will be used for the [title] and [subtitle]. Otherwise,
/// defaults to color of the [TextTheme.titleMedium].
///
/// See also:
///
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
/// [ExpansionTileThemeData].
final Color? collapsedTextColor;
/// The tile's border shape when the sublist is expanded.
///
/// If this property is null, the [ExpansionTileThemeData.shape] is used. If that
/// is also null, a [Border] with vertical sides default to [ThemeData.dividerColor] is used
///
/// See also:
///
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
/// [ExpansionTileThemeData].
final ShapeBorder? shape;
/// The tile's border shape when the sublist is collapsed.
///
/// If this property is null, the [ExpansionTileThemeData.collapsedShape] is used. If that
/// is also null, a [Border] with vertical sides default to Color [Colors.transparent] is used
///
/// See also:
///
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
/// [ExpansionTileThemeData].
final ShapeBorder? collapsedShape;
/// {@macro flutter.material.Material.clipBehavior}
///
/// If this is not null and a custom collapsed or expanded shape is provided,
/// the value of [clipBehavior] will be used to clip the expansion tile.
///
/// If this property is null, the [ExpansionTileThemeData.clipBehavior] is used. If that
/// is also null, defaults to [Clip.antiAlias].
///
/// See also:
///
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
/// [ExpansionTileThemeData].
final Clip? clipBehavior;
/// Typically used to force the expansion arrow icon to the tile's leading or trailing edge.
///
/// By default, the value of [controlAffinity] is [ListTileControlAffinity.platform],
/// which means that the expansion arrow icon will appear on the tile's trailing edge.
final ListTileControlAffinity? controlAffinity;
/// If provided, the controller can be used to expand and collapse tiles.
///
/// In cases where control over the tile's state is needed from a callback
/// triggered by a widget within the tile, [ExpansibleController.of] may be
/// more convenient than supplying a controller.
final ExpansionTileController? controller;
/// {@macro flutter.material.ListTile.dense}
final bool? dense;
final BorderRadius? borderRadius;
/// Defines how compact the expansion tile's layout will be.
///
/// {@macro flutter.material.themedata.visualDensity}
final VisualDensity? visualDensity;
/// {@macro flutter.material.ListTile.minTileHeight}
final double? minTileHeight;
/// {@macro flutter.material.ListTile.enableFeedback}
final bool? enableFeedback;
/// Whether this expansion tile is interactive.
///
/// If false, the internal [ListTile] will be disabled, changing its
/// appearance according to the theme and disabling user interaction.
///
/// Even if disabled, the expansion can still be toggled programmatically
/// through an [ExpansionTileController].
final bool enabled;
/// Used to override the expansion animation curve and duration.
///
/// If [AnimationStyle.duration] is provided, it will be used to override
/// the expansion animation duration. If it is null, then [AnimationStyle.duration]
/// from the [ExpansionTileThemeData.expansionAnimationStyle] will be used.
/// Otherwise, defaults to 200ms.
///
/// If [AnimationStyle.curve] is provided, it will be used to override
/// the expansion animation curve. If it is null, then [AnimationStyle.curve]
/// from the [ExpansionTileThemeData.expansionAnimationStyle] will be used.
/// Otherwise, defaults to [Curves.easeIn].
///
/// If [AnimationStyle.reverseCurve] is provided, it will be used to override
/// the collapse animation curve. If it is null, then [AnimationStyle.reverseCurve]
/// from the [ExpansionTileThemeData.expansionAnimationStyle] will be used.
/// Otherwise, the same curve will be used as for expansion.
///
/// To disable the theme animation, use [AnimationStyle.noAnimation].
///
/// {@tool dartpad}
/// This sample showcases how to override the [ExpansionTile] expansion
/// animation curve and duration using [AnimationStyle].
///
/// ** See code in examples/api/lib/material/expansion_tile/expansion_tile.2.dart **
/// {@end-tool}
final AnimationStyle? expansionAnimationStyle;
/// Whether to add button:true to the semantics if onTap is provided.
/// This is a temporary flag to help changing the behavior of ListTile onTap semantics.
///
// TODO(hangyujin): Remove this flag after fixing related g3 tests and flipping
// the default value to true.
final bool internalAddSemanticForOnTap;
@override
State<CustomExpansionTile> createState() => _customExpansionTileState();
}
class _customExpansionTileState extends State<CustomExpansionTile> {
static final Animatable<double> _easeInTween = CurveTween(
curve: Curves.easeIn);
static final Animatable<double> _easeOutTween = CurveTween(
curve: Curves.easeOut);
static final Animatable<double> _halfTween = Tween<double>(
begin: 0.0, end: 0.5);
final ShapeBorderTween _borderTween = ShapeBorderTween();
final ColorTween _headerColorTween = ColorTween();
final ColorTween _iconColorTween = ColorTween();
final ColorTween _backgroundColorTween = ColorTween();
late Animation<double> _iconTurns;
late Animation<ShapeBorder?> _border;
late Animation<Color?> _headerColor;
late Animation<Color?> _iconColor;
late Animation<Color?> _backgroundColor;
late ExpansionTileThemeData _expansionTileTheme;
late ExpansionTileController _tileController;
Timer? _timer;
late Curve _curve;
late Curve? _reverseCurve;
late Duration _duration;
@override
void initState() {
super.initState();
_curve = Curves.easeIn;
_duration = _kExpand;
_tileController = widget.controller ?? ExpansionTileController();
if (widget.initiallyExpanded) {
_tileController.expand();
}
_tileController.addListener(_onExpansionChanged);
}
@override
void dispose() {
_tileController.removeListener(_onExpansionChanged);
if (widget.controller == null) {
_tileController.dispose();
}
_timer?.cancel();
_timer = null;
super.dispose();
}
void _onExpansionChanged() {
final TextDirection textDirection = WidgetsLocalizations
.of(context)
.textDirection;
final MaterialLocalizations localizations = MaterialLocalizations.of(
context);
final String stateHint = _tileController.isExpanded
? localizations.collapsedHint
: localizations.expandedHint;
if (defaultTargetPlatform == TargetPlatform.iOS) {
// TODO(tahatesser): This is a workaround for VoiceOver interrupting
// semantic announcements on iOS. https://github.com/flutter/flutter/issues/122101.
_timer?.cancel();
_timer = Timer(const Duration(seconds: 1), () {
SemanticsService.announce(stateHint, textDirection);
_timer?.cancel();
_timer = null;
});
} else {
SemanticsService.announce(stateHint, textDirection);
}
widget.onExpansionChanged?.call(_tileController.isExpanded);
}
// Platform or null affinity defaults to trailing.
ListTileControlAffinity _effectiveAffinity() {
final ListTileThemeData listTileTheme = ListTileTheme.of(context);
final ListTileControlAffinity affinity =
widget.controlAffinity ?? listTileTheme.controlAffinity ??
ListTileControlAffinity.trailing;
switch (affinity) {
case ListTileControlAffinity.leading:
return ListTileControlAffinity.leading;
case ListTileControlAffinity.trailing:
case ListTileControlAffinity.platform:
return ListTileControlAffinity.trailing;
}
}
Widget? _buildIcon(BuildContext context, Animation<double> animation) {
_iconTurns = animation.drive(_halfTween.chain(_easeInTween));
return RotationTransition(
turns: _iconTurns, child: const Icon(Icons.expand_more));
}
Widget? _buildLeadingIcon(BuildContext context, Animation<double> animation) {
if (_effectiveAffinity() != ListTileControlAffinity.leading) {
return null;
}
return _buildIcon(context, animation);
}
Widget? _buildTrailingIcon(BuildContext context,
Animation<double> animation) {
if (_effectiveAffinity() != ListTileControlAffinity.trailing) {
return null;
}
return _buildIcon(context, animation);
}
Widget _buildHeader(BuildContext context, Animation<double> animation) {
_iconColor = animation.drive(_iconColorTween.chain(_easeInTween));
_headerColor = animation.drive(_headerColorTween.chain(_easeInTween));
final ThemeData theme = Theme.of(context);
final MaterialLocalizations localizations = MaterialLocalizations.of(
context);
final String onTapHint = _tileController.isExpanded
? localizations.expansionTileExpandedTapHint
: localizations.expansionTileCollapsedTapHint;
String? semanticsHint;
switch (theme.platform) {
case TargetPlatform.iOS:
case TargetPlatform.macOS:
semanticsHint = _tileController.isExpanded
? '${localizations.collapsedHint}\n ${localizations
.expansionTileExpandedHint}'
: '${localizations.expandedHint}\n ${localizations
.expansionTileCollapsedHint}';
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
break;
}
return Semantics(
hint: semanticsHint,
onTapHint: onTapHint,
child: ListTileTheme.merge(
iconColor: _iconColor.value ?? _expansionTileTheme.iconColor,
textColor: _headerColor.value,
child: ListTile(
enabled: widget.enabled,
onTap: _tileController.isExpanded
? _tileController.collapse
: _tileController.expand,
dense: widget.dense,
visualDensity: widget.visualDensity,
enableFeedback: widget.enableFeedback,
contentPadding: widget.tilePadding ?? _expansionTileTheme.tilePadding,
leading: widget.leading ?? _buildLeadingIcon(context, animation),
title: widget.title,
subtitle: widget.subtitle,
trailing: widget.showTrailingIcon
? widget.trailing ?? _buildTrailingIcon(context, animation)
: null,
minTileHeight: widget.minTileHeight,
internalAddSemanticForOnTap: widget.internalAddSemanticForOnTap,
),
),
);
}
Widget _buildBody(BuildContext context, Animation<double> animation) {
return Align(
alignment:
widget.expandedAlignment ?? _expansionTileTheme.expandedAlignment ??
Alignment.center,
child: Padding(
padding: widget.childrenPadding ??
_expansionTileTheme.childrenPadding ?? EdgeInsets.zero,
child: Column(
crossAxisAlignment: widget.expandedCrossAxisAlignment ??
CrossAxisAlignment.center,
children: widget.children,
),
),
);
}
Widget _buildExpansible(BuildContext context,
Widget header,
Widget body,
Animation<double> animation,) {
_backgroundColor =
animation.drive(_backgroundColorTween.chain(_easeOutTween));
_border = animation.drive(_borderTween.chain(_easeOutTween));
final Color backgroundColor =
_backgroundColor.value ?? _expansionTileTheme.backgroundColor ??
Colors.transparent;
final ShapeBorder expansionTileBorder = _border.value ??
RoundedRectangleBorder(
borderRadius: widget.borderRadius ?? BorderRadius.circular(5),
side: BorderSide(color: Colors.transparent),
);
final Clip clipBehavior =
widget.clipBehavior ?? _expansionTileTheme.clipBehavior ??
Clip.antiAlias;
final Decoration decoration = ShapeDecoration(
color: backgroundColor,
shape: expansionTileBorder,
);
final Widget tile = Padding(
padding: decoration.padding,
child: Column(
mainAxisSize: MainAxisSize.min, children: <Widget>[header, body]),
);
final bool isShapeProvided =
widget.shape != null ||
_expansionTileTheme.shape != null ||
widget.collapsedShape != null ||
_expansionTileTheme.collapsedShape != null;
if (isShapeProvided) {
return Material(
clipBehavior: clipBehavior,
color: backgroundColor,
shape: expansionTileBorder,
child: tile,
);
}
return DecoratedBox(decoration: decoration, child: tile);
}
@override
void didUpdateWidget(covariant CustomExpansionTile oldWidget) {
super.didUpdateWidget(oldWidget);
final ThemeData theme = Theme.of(context);
_expansionTileTheme = ExpansionTileTheme.of(context);
final ExpansionTileThemeData defaults = theme.useMaterial3
? _ExpansionTileDefaultsM3(context)
: _ExpansionTileDefaultsM2(context);
if (widget.collapsedShape != oldWidget.collapsedShape ||
widget.shape != oldWidget.shape) {
_updateShapeBorder(theme);
}
if (widget.collapsedTextColor != oldWidget.collapsedTextColor ||
widget.textColor != oldWidget.textColor) {
_updateHeaderColor(defaults);
}
if (widget.collapsedIconColor != oldWidget.collapsedIconColor ||
widget.iconColor != oldWidget.iconColor) {
_updateIconColor(defaults);
}
if (widget.backgroundColor != oldWidget.backgroundColor ||
widget.collapsedBackgroundColor != oldWidget.collapsedBackgroundColor) {
_updateBackgroundColor();
}
if (widget.expansionAnimationStyle != oldWidget.expansionAnimationStyle) {
_updateAnimationDuration();
_updateHeightFactorCurve();
}
}
@override
void didChangeDependencies() {
final ThemeData theme = Theme.of(context);
_expansionTileTheme = ExpansionTileTheme.of(context);
final ExpansionTileThemeData defaults = theme.useMaterial3
? _ExpansionTileDefaultsM3(context)
: _ExpansionTileDefaultsM2(context);
_updateAnimationDuration();
_updateShapeBorder(theme);
_updateHeaderColor(defaults);
_updateIconColor(defaults);
_updateBackgroundColor();
_updateHeightFactorCurve();
super.didChangeDependencies();
}
void _updateAnimationDuration() {
_duration =
widget.expansionAnimationStyle?.duration ??
_expansionTileTheme.expansionAnimationStyle?.duration ??
const Duration(milliseconds: 200);
}
void _updateShapeBorder(ThemeData theme) {
final BorderRadius radius = widget.borderRadius ?? BorderRadius.circular(5);
_borderTween
..begin = widget.collapsedShape ??
_expansionTileTheme.collapsedShape ??
RoundedRectangleBorder(
borderRadius: radius,
side: const BorderSide(color: Colors.transparent),
)
..end = widget.shape ??
_expansionTileTheme.shape ??
RoundedRectangleBorder(
borderRadius: radius,
side: BorderSide(color:Colors.transparent),
);
}
void _updateHeaderColor(ExpansionTileThemeData defaults) {
_headerColorTween
..begin =
widget.collapsedTextColor ??
_expansionTileTheme.collapsedTextColor ??
defaults.collapsedTextColor
..end = widget.textColor ?? _expansionTileTheme.textColor ??
defaults.textColor;
}
void _updateIconColor(ExpansionTileThemeData defaults) {
_iconColorTween
..begin =
widget.collapsedIconColor ??
_expansionTileTheme.collapsedIconColor ??
defaults.collapsedIconColor
..end = widget.iconColor ?? _expansionTileTheme.iconColor ??
defaults.iconColor;
}
void _updateBackgroundColor() {
_backgroundColorTween
..begin = widget.collapsedBackgroundColor ??
_expansionTileTheme.collapsedBackgroundColor
..end = widget.backgroundColor ?? _expansionTileTheme.backgroundColor;
}
void _updateHeightFactorCurve() {
_curve =
widget.expansionAnimationStyle?.curve ??
_expansionTileTheme.expansionAnimationStyle?.curve ??
Curves.easeIn;
_reverseCurve =
widget.expansionAnimationStyle?.reverseCurve ??
_expansionTileTheme.expansionAnimationStyle?.reverseCurve;
}
@override
Widget build(BuildContext context) {
return Expansible(
controller: _tileController,
curve: _curve,
duration: _duration,
reverseCurve: _reverseCurve,
maintainState: widget.maintainState,
headerBuilder: _buildHeader,
bodyBuilder: _buildBody,
expansibleBuilder: _buildExpansible,
);
}
}
class _ExpansionTileDefaultsM2 extends ExpansionTileThemeData {
_ExpansionTileDefaultsM2(this.context);
final BuildContext context;
late final ThemeData _theme = Theme.of(context);
late final ColorScheme _colorScheme = _theme.colorScheme;
@override
Color? get textColor => _colorScheme.primary;
@override
Color? get iconColor => _colorScheme.primary;
@override
Color? get collapsedTextColor => _theme.textTheme.titleMedium!.color;
@override
Color? get collapsedIconColor => _theme.unselectedWidgetColor;
}
// BEGIN GENERATED TOKEN PROPERTIES - ExpansionTile
// Do not edit by hand. The code between the "BEGIN GENERATED" and
// "END GENERATED" comments are generated from data in the Material
// Design token database by the script:
// dev/tools/gen_defaults/bin/gen_defaults.dart.
// dart format off
class _ExpansionTileDefaultsM3 extends ExpansionTileThemeData {
_ExpansionTileDefaultsM3(this.context);
final BuildContext context;
late final ThemeData _theme = Theme.of(context);
late final ColorScheme _colors = _theme.colorScheme;
@override
Color? get textColor => _colors.onSurface;
@override
Color? get iconColor => _colors.primary;
@override
Color? get collapsedTextColor => _colors.onSurface;
@override
Color? get collapsedIconColor => _colors.onSurfaceVariant;
}
// dart format on
// END GENERATED TOKEN PROPERTIES - ExpansionTile

View File

@@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
class CustomText extends StatelessWidget {
final FontWeight? weight;
final Color? color;
final double? size;
final String text;
final int? maxLines;
final TextOverflow? overflow;
const CustomText({
Key? key,
this.weight,
this.color,
this.size,
required this.text,
this.maxLines,
this.overflow,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Text(
text,
style: TextStyle(
fontWeight: FontWeight.lerp(
weight,
FontWeight.values[
(FontWeight.values.indexOf(weight??FontWeight.w400) + 1)
.clamp(0, FontWeight.values.length - 1) // prevent overflow
],
0.5, // t: pick between 0.0 and 1.0
),
color: color,
fontSize: size,
),
maxLines: maxLines,
overflow: overflow,
);
}
}

View File

@@ -0,0 +1,64 @@
import 'package:flutter/material.dart';
import 'package:citycards_customer/common_package/custom_text.dart';
class CustomTextField extends StatelessWidget {
final String label;
final String hint;
final TextEditingController controller;
final int? maxLines;
const CustomTextField({
super.key,
required this.label,
required this.hint,
required this.controller,
this.maxLines =1
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomText(text: label, size: 14),
const SizedBox(height: 6),
SizedBox(
height: maxLines == 1 ? 42 : null,
child: TextField(
controller: controller,
maxLines: maxLines,
decoration: InputDecoration(
hintText: hint,
hintStyle: const TextStyle(
fontSize: 12,
color: Color(0xFF8E8E8E),
),
filled: true,
fillColor: const Color(0xFFFFF5F5),
contentPadding: const EdgeInsets.symmetric(
horizontal: 24,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: Color(0xBBC83B61).withOpacity(0.4),
width: .4,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(
color: Color(0xFFF95F62),
width: 1,
),
),
),
),
),
],
),
);
}
}

View File

@@ -0,0 +1,275 @@
import 'package:flutter/material.dart';
import 'package:citycards_customer/common_package/custom_text.dart';
import 'package:citycards_customer/common_package/custom_textfield.dart';
class ContactUsPage extends StatelessWidget {
const ContactUsPage({super.key});
@override
Widget build(BuildContext context) {
final TextEditingController firstNameController = TextEditingController();
final TextEditingController lastNameController = TextEditingController();
final TextEditingController emailController = TextEditingController();
final TextEditingController phoneController = TextEditingController();
final TextEditingController messageController = TextEditingController();
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header bar
Container(
padding: const EdgeInsets.only(bottom: 10),
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(color: Color(0xFFD9D9D9), width: 0.7),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Image.asset("assets/logo/logo_city_cards.png", scale: 4),
Image.asset("assets/icons/shopping_cart.png", scale: 4),
],
),
),
const SizedBox(height: 22),
// Back + Title
Row(
children: [
GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Icon(Icons.arrow_back, size: 24),
),
SizedBox(width: 8),
Text(
"Contact Us",
style: TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
),
],
),
const SizedBox(height: 22),
CustomText(
text:
"You can get in touch with us through the below platforms. Our team will contact you shortly",
size: 14,
color: Colors.black.withOpacity(.6),
),
const SizedBox(height: 20),
// Customer Support Section
Container(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 16,
),
decoration: BoxDecoration(
color: Color(0x00000005).withOpacity(.02),
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const CustomText(
text: "Customer Support",
size: 18,
weight: FontWeight.w500,
),
const SizedBox(height: 16),
_supportBox(
icon: Icons.phone,
title: "Contact Number",
subtitle: "+1012 3456 789",
action: "Tap to call",
),
const SizedBox(height: 12),
_supportBox(
icon: Icons.email_rounded,
title: "Email",
subtitle: "citycards24@gmail.com",
action: "Tap to email",
),
const SizedBox(height: 12),
_supportBox(
icon: Icons.location_on,
title: "Location",
subtitle:
"132 Dartmouth Street Boston, Massachusetts 02156 United States",
action: "View on map",
),
],
),
),
const SizedBox(height: 24),
// Text fields
CustomTextField(
label: "First Name",
hint: "Enter your first name",
controller: firstNameController,
),
CustomTextField(
label: "Last Name",
hint: "Enter your last name",
controller: lastNameController,
),
CustomTextField(
label: "Email",
hint: "Enter your email address",
controller: emailController,
),
CustomTextField(
label: "Phone Number",
hint: "Enter your phone number",
controller: phoneController,
),
CustomTextField(
label: "Description",
hint: "Write your message here",
maxLines: 4,
controller: messageController,
),
// _descriptionField(messageController),
const SizedBox(height: 24),
// Submit Button
SizedBox(
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFF95F62),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(38),
),
padding: const EdgeInsets.symmetric(vertical: 6),
),
onPressed: () {},
child: const CustomText(
text: "Submit Ticket",
size: 16,
weight: FontWeight.w500,
color: Colors.white,
),
),
),
const SizedBox(height: 20),
],
),
),
),
);
}
// --- Support Info Box ---
Widget _supportBox({
required IconData icon,
required String title,
required String subtitle,
required String action,
}) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(color: const Color(0xFFF95F62), width: 0.8),
color: Colors.white,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(icon, color: const Color(0xFFF95F62), size: 32),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomText(
text: title,
size: 11,
weight: FontWeight.w600,
color: Color(0x00000000).withOpacity(.6),
),
const SizedBox(height: 6),
Text(
subtitle,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Colors.black,
),
),
const SizedBox(height: 2),
Text(
action,
style: TextStyle(
fontSize: 11,
color: Color(0xFF000000).withOpacity(.4),
fontWeight: FontWeight.w400,
),
),
],
),
),
],
),
);
}
// --- Description Field ---
Widget _descriptionField(TextEditingController controller) {
return Padding(
padding: const EdgeInsets.only(bottom: 12, left: 12, right: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const CustomText(text: "Description", size: 14),
const SizedBox(height: 6),
TextField(
controller: controller,
maxLines: 4,
decoration: InputDecoration(
hintText: "Write your message here",
hintStyle: const TextStyle(
fontSize: 12,
color: Color(0xFF8E8E8E),
),
filled: true,
fillColor: const Color(0xFFFFF5F5),
contentPadding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 12,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: const Color(0xBBC83B61).withOpacity(0.4),
width: .4,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(
color: Color(0xFFF95F62),
width: 1,
),
),
),
),
],
),
);
}
}

View File

@@ -1,3 +1,9 @@
import 'package:citycards_customer/Profile/profile_page_view.dart';
import 'package:citycards_customer/contact_us/contact_us_view.dart';
import 'package:citycards_customer/edit_profile/edit_profile_view.dart';
import 'package:citycards_customer/faq/faq_view.dart';
import 'package:citycards_customer/privacy/privacy_view.dart';
import 'package:citycards_customer/terms_and_condition/terms_and_condition_view.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../home/views/home_page_view.dart';
@@ -19,11 +25,47 @@ class AppRouter {
},
);
case RouteConstants.profile:
return MaterialPageRoute(
builder: (_) {
return const ProfilePage();
},
);
case RouteConstants.editProfile:
return MaterialPageRoute(
builder: (_) {
return const EditProfilePage();
},
);
case RouteConstants.contactUs:
return MaterialPageRoute(
builder: (_) {
return const ContactUsPage();
},
);
case RouteConstants.faq:
return MaterialPageRoute(
builder: (_) {
return const FaqPage();
},
);
case RouteConstants.termsAndCondition:
return MaterialPageRoute(
builder: (_) {
return const TermsAndCondition();
},
);
case RouteConstants.privacyPolicy:
return MaterialPageRoute(
builder: (_) {
return const PrivacyPolicyPage();
},
);
default:
return MaterialPageRoute(
builder: (_) => const Scaffold(
body: Center(child: Text('404 - Page Not Found')),
),
builder: (_) =>
const Scaffold(body: Center(child: Text('404 - Page Not Found'))),
);
}
}

View File

@@ -1,3 +1,12 @@
class RouteConstants {
static const String home = '/home';
/* ****************************** Profile Section **************************/
static const String profile = '/profile';
static const String editProfile = '/editProfile';
static const String contactUs = '/contactUs';
static const String termsAndCondition = '/termsAndCondition';
static const String privacyPolicy = '/privacyPolicy';
static const String faq = '/faq';
}

View File

@@ -0,0 +1,197 @@
import 'package:citycards_customer/common_package/custom_textfield.dart';
import 'package:flutter/material.dart';
import 'package:citycards_customer/common_package/custom_text.dart';
class EditProfilePage extends StatelessWidget {
const EditProfilePage({super.key});
@override
Widget build(BuildContext context) {
final TextEditingController firstNameController = TextEditingController();
final TextEditingController lastNameController = TextEditingController();
final TextEditingController emailController = TextEditingController();
final TextEditingController phoneController = TextEditingController();
final TextEditingController addressController = TextEditingController();
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Header
Container(
padding: const EdgeInsets.only(bottom: 10),
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(color: Color(0xFFD9D9D9), width: 0.7),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Image.asset("assets/logo/logo_city_cards.png", scale: 4),
Image.asset("assets/icons/shopping_cart.png", scale: 4),
],
),
),
const SizedBox(height: 22),
// Back + title
Row(
children: [
GestureDetector(
onTap: (){
Navigator.pop(context);
},
child: Icon(Icons.arrow_back, size: 24)),
SizedBox(width: 8),
Text(
"Edit Profile",
style: TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
),
],
),
const SizedBox(height: 33),
// Profile Image
const CircleAvatar(
radius: 38,
backgroundImage: AssetImage("assets/images/profile_img.png"),
),
const SizedBox(height: 18),
const Text(
"Change Profile Picture",
style: TextStyle(
fontSize: 12,
color: Color(0xFFF95F62),
fontWeight: FontWeight.w400,
),
),
const SizedBox(height: 40),
// Personal Information
Align(
alignment: Alignment.centerLeft,
child: CustomText(
text: "Personal Information",
size: 18,
weight: FontWeight.w500,
),
),
const SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: CustomTextField(
label: "First Name",
hint: "Enter your first name",
controller: firstNameController,
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: CustomTextField(
label: "Last Name",
hint: "Enter your last name",
controller: lastNameController,
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: CustomTextField(
label: "Email",
hint: "Enter your email address",
controller: emailController,
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: CustomTextField(
label: "Phone Number",
hint: "Enter your phone number",
controller: phoneController,
),
),
const SizedBox(height: 2),
// Location Details
Align(
alignment: Alignment.centerLeft,
child: CustomText(
text: "Location Details",
size: 18,
weight: FontWeight.w500,
),
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: CustomTextField(
label: "Address 1",
hint: "Enter address manually or tap to search",
controller: addressController,
),
),
const SizedBox(height: 26),
// Buttons
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: OutlinedButton(
style: OutlinedButton.styleFrom(
foregroundColor: const Color(0xFFF95F62),
side: const BorderSide(color: Colors.transparent),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(38),
),
padding: const EdgeInsets.symmetric(vertical: 12),
),
onPressed: () {},
child: const Text(
"Cancel",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
),
const SizedBox(width: 16),
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFF95F62),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(38),
),
padding: const EdgeInsets.symmetric(vertical: 6),
),
onPressed: () {},
child: const Text(
"Save",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
),
),
],
),
const SizedBox(height: 20),
],
),
),
),
);
}
}

185
lib/faq/faq_view.dart Normal file
View File

@@ -0,0 +1,185 @@
import 'package:citycards_customer/common_package/custom_expansiontile.dart';
import 'package:citycards_customer/common_package/custom_text.dart';
import 'package:flutter/material.dart';
class FaqPage extends StatelessWidget {
const FaqPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.only(bottom: 10),
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(color: Color(0xFFD9D9D9), width: 0.7),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Image.asset("assets/logo/logo_city_cards.png", scale: 4),
Image.asset("assets/icons/shopping_cart.png", scale: 4),
],
),
),
const SizedBox(height: 22),
// Back + Title
Row(
children: [
GestureDetector(
onTap: (){
Navigator.pop(context);
},
child: Icon(Icons.arrow_back, size: 24)),
SizedBox(width: 8),
Text(
"FAQ",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
],
),
const SizedBox(height: 34),
FAQSection(title: "🧭 General FAQs", faqs: generalFAQs),
SizedBox(height: 20),
FAQSection(title: "✈️ Booking & Planning", faqs: bookingFaq),
SizedBox(height: 20),
FAQSection(title: "🌍 Discover & Explore", faqs: discoverFAQs),
],
),
),
),
),
);
}
}
// Model for FAQ
class FAQItem {
final String question;
final String answer;
FAQItem({required this.question, required this.answer});
}
// Sample FAQ data
final List<FAQItem> generalFAQs = [
FAQItem(
question: "What is CityCards?",
answer:
"You can browse without an account, but signing up allows you to save trips, sync data, and get personalized recommendations.",
),
FAQItem(
question: "Is the app free to use?",
answer:
"You can browse without an account, but signing up allows you to save trips, sync data, and get personalized recommendations.",
),
FAQItem(
question: "Do I need an account to use the app?",
answer:
"You can browse without an account, but signing up allows you to save trips, sync data, and get personalized recommendations.",
),
];
final List<FAQItem> discoverFAQs = [
FAQItem(
question: "How does the app recommend destinations?",
answer:
"You can browse without an account, but signing up allows you to save trips, sync data, and get personalized recommendations.",
),
FAQItem(
question: "Can I create a custom itinerary?",
answer:
"You can browse without an account, but signing up allows you to save trips, sync data, and get personalized recommendations.",
),
FAQItem(
question: "Does the app work offline?",
answer:
"You can browse without an account, but signing up allows you to save trips, sync data, and get personalized recommendations.",
),
];
final List<FAQItem> bookingFaq = [
FAQItem(
question: "Can I modify or cancel my bookings?",
answer:
"You can browse without an account, but signing up allows you to save trips, sync data, and get personalized recommendations.",
),
FAQItem(
question: "Can I plan multi-city trips?",
answer:
"You can browse without an account, but signing up allows you to save trips, sync data, and get personalized recommendations.",
),
FAQItem(
question: "Can I book hotels through the app?",
answer:
"You can browse without an account, but signing up allows you to save trips, sync data, and get personalized recommendations.",
),
];
// Widget for FAQ section
Widget FAQSection({required String title, required List<FAQItem> faqs}) {
return Container(
padding: EdgeInsets.symmetric(vertical: 12, horizontal: 8),
decoration: BoxDecoration(
border: Border.all(color: Color(0xFFF95F62)),
borderRadius: BorderRadius.circular(10),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Section heading
CustomText(
text: title,
size: 16,
weight: FontWeight.w500,
color: Color(0xFF212121),
),
SizedBox(height: 12),
// Dynamic list of questions
Column(
children: faqs.map((faq) {
int index = faqs.indexOf(faq);
return Column(
children: [
CustomExpansionTile(
minTileHeight: 42,
borderRadius: BorderRadius.circular(5),
backgroundColor: Color(0xFFFEE7E7),
collapsedBackgroundColor: Color(0xFFFEE7E7),
tilePadding: EdgeInsets.symmetric(
horizontal: 14,
vertical: 0,
),
childrenPadding: EdgeInsets.all(12),
title: Text(faq.question, style: TextStyle(fontSize: 14)),
children: [
Text(
faq.answer,
style: TextStyle(color: Color(0xFF5C5C5C), fontSize: 14),
),
],
),
if (index != faqs.length - 1) SizedBox(height: 8), // spacing
],
);
}).toList(),
),
],
),
);
}

View File

@@ -1,3 +1,4 @@
import 'package:citycards_customer/core/route_constants.dart';
import 'package:citycards_customer/home/views/explore_cities_card.dart';
import 'package:flutter/material.dart';
@@ -132,11 +133,16 @@ class _HomePageState extends State<HomePage> {
),
),
const SizedBox(width: 8),
const CircleAvatar(
GestureDetector(
onTap: (){
Navigator.pushNamed(context, RouteConstants.profile);
},
child: const CircleAvatar(
backgroundColor: Color(0xffFFDFDF),
backgroundImage:
AssetImage("assets/images/profile_img.png"),
),
),
],
),
],

View File

@@ -0,0 +1,69 @@
import 'package:citycards_customer/common_package/custom_text.dart';
import 'package:flutter/material.dart';
class PrivacyPolicyPage extends StatelessWidget {
const PrivacyPolicyPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: Column(
children: [
Container(
padding: const EdgeInsets.only(bottom: 10),
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(color: Color(0xFFD9D9D9), width: 0.7),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Image.asset("assets/logo/logo_city_cards.png", scale: 4),
Image.asset("assets/icons/shopping_cart.png", scale: 4),
],
),
),
const SizedBox(height: 22),
// Back + Title
Row(
children: [
GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Icon(Icons.arrow_back, size: 24),
),
SizedBox(width: 8),
Text(
"Privacy Policy",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
],
),
SizedBox(height: 32),
CustomText(
text:
"Your use of our website is governed by the following terms and conditions (“Terms of Use”), as well as the CARDONE CAPITAL Privacy Policy and other operating rules, minimum qualifications and cautions posted throughout the website or presented to you individually during the course of your use of the website (collectively, the “Terms”). \n\n"
"The Terms govern your use of the website and CARDONE CAPITAL reserves the right to update or replace the Terms any time without notice. You are advised to review the Terms for any changes when you visit the website even if you have not received a notification of changes as you are bound by them even if you have not reviewed them. \n\n"
"Your viewing and use of the website after such change constitutes your acceptance of the Terms and any changes to such terms. If at any time you do not want to be bound by the Terms you should logout, exit and cease using the website immediately.",
size: 14,
weight: FontWeight.w400,
color: Color(0xFF000000).withOpacity(.6),
),
],
),
),
),
),
);
}
}

View File

@@ -0,0 +1,213 @@
import 'package:citycards_customer/common_package/custom_text.dart';
import 'package:citycards_customer/core/route_constants.dart';
import 'package:flutter/material.dart';
class ProfilePage extends StatelessWidget {
const ProfilePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.only(bottom: 10),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(color: Color(0xFFD9D9D9), width: .7),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Image.asset("assets/logo/logo_city_cards.png", scale: 4),
Image.asset("assets/icons/shopping_cart.png", scale: 4),
],
),
),
SizedBox(height: 22),
Row(
children: [
GestureDetector(
onTap: (){
Navigator.pop(context);
},
child: Icon(Icons.arrow_back, size: 24)),
SizedBox(width: 8),
Text("My profile", style: TextStyle(fontSize: 12)),
],
),
SizedBox(height: 29),
// Profile Image and Name
Row(
children: [
const CircleAvatar(
radius: 38,
backgroundImage: AssetImage(
"assets/images/profile_img.png",
),
),
SizedBox(width: 16),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Laysha Adams',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
// SizedBox(height: 4,),
Row(
children: [
Icon(
Icons.location_on_sharp,
color: Color(0xFF8E8E8E),
size: 14,
),
SizedBox(width: 4),
const Text(
'Louisiana, United States',
style: TextStyle(
fontSize: 12,
color: Color(0xFF8E8E8E),
),
),
],
),
],
),
],
),
const SizedBox(height: 30),
// Account Settings Section
Align(
alignment: Alignment.centerLeft,
child: CustomText(
text: "Account Settings",
weight: FontWeight.w500,
size: 18,
),
),
const SizedBox(height: 10),
_buildListTile(
icon: "assets/icons/user_profile.png",
title: 'Edit profile',
onTap: () {
Navigator.pushNamed(context, RouteConstants.editProfile);
},
),
_buildListTile(
icon: "assets/icons/change_language.png",
title: 'Change language',
onTap: () {},
),
const SizedBox(height: 24),
// Support & Legal Section
Align(
alignment: Alignment.centerLeft,
child: CustomText(
text: "Support & Legal",
weight: FontWeight.w500,
size: 18,
),
),
const SizedBox(height: 10),
_buildListTile(
icon: "assets/icons/contact_us.png",
title: 'Contact Us',
onTap: () {
Navigator.pushNamed(context, RouteConstants.contactUs);
},
),
_buildListTile(
icon: "assets/icons/terms_and_condition.png",
title: 'Terms and Conditions',
onTap: () {
Navigator.pushNamed(
context,
RouteConstants.termsAndCondition,
);
},
),
_buildListTile(
icon: "assets/icons/faq.png",
title: 'FAQ',
onTap: () {
Navigator.pushNamed(context, RouteConstants.faq);
},
),
_buildListTile(
icon: "assets/icons/privacy.png",
title: 'Privacy Policy',
onTap: () {
Navigator.pushNamed(context, RouteConstants.privacyPolicy);
},
),
const SizedBox(height: 22),
// Logout Button
SizedBox(
width: double.infinity,
child: OutlinedButton(
style: OutlinedButton.styleFrom(
foregroundColor: Color(0xFFF95F62),
side: const BorderSide(color: Color(0xFFF95F62)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(38),
),
padding: const EdgeInsets.symmetric(vertical: 6),
),
onPressed: () {},
child: const Text(
'Log out',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
),
),
],
),
),
),
);
}
Widget _buildListTile({
required String icon,
required String title,
required VoidCallback onTap,
}) {
return Container(
height: 64,
decoration: BoxDecoration(
border: Border.all(color: Colors.black.withOpacity(.10)),
borderRadius: BorderRadius.circular(15),
),
margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
child: ListTile(
leading: Image.asset(icon, scale: 4),
title: Text(
title,
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
onTap: onTap,
),
);
}
}

View File

@@ -0,0 +1,69 @@
import 'package:citycards_customer/common_package/custom_text.dart';
import 'package:flutter/material.dart';
class TermsAndCondition extends StatelessWidget {
const TermsAndCondition({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: Column(
children: [
Container(
padding: const EdgeInsets.only(bottom: 10),
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(color: Color(0xFFD9D9D9), width: 0.7),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Image.asset("assets/logo/logo_city_cards.png", scale: 4),
Image.asset("assets/icons/shopping_cart.png", scale: 4),
],
),
),
const SizedBox(height: 22),
// Back + Title
Row(
children: [
GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Icon(Icons.arrow_back, size: 24),
),
SizedBox(width: 8),
Text(
"Terms and Conditons",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
],
),
SizedBox(height: 32),
CustomText(
text:
"Your use of our website is governed by the following terms and conditions (“Terms of Use”), as well as the CARDONE CAPITAL Privacy Policy and other operating rules, minimum qualifications and cautions posted throughout the website or presented to you individually during the course of your use of the website (collectively, the “Terms”). \n\n"
"The Terms govern your use of the website and CARDONE CAPITAL reserves the right to update or replace the Terms any time without notice. You are advised to review the Terms for any changes when you visit the website even if you have not received a notification of changes as you are bound by them even if you have not reviewed them. \n\n"
"Your viewing and use of the website after such change constitutes your acceptance of the Terms and any changes to such terms. If at any time you do not want to be bound by the Terms you should logout, exit and cease using the website immediately.",
size: 14,
weight: FontWeight.w400,
color: Color(0xFF000000).withOpacity(.6),
),
],
),
),
),
),
);
}
}