D1APP-24 home page (static)

This commit is contained in:
MohammadTaha Basiri 2021-12-12 15:41:04 +03:30
parent 5edc7f825f
commit 5925e71beb
26 changed files with 604 additions and 10 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 94 KiB

View File

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 124 KiB

View File

@ -60,9 +60,28 @@ class DesignConfig {
static const BorderRadius lowBorderRadius = BorderRadius.all(
Radius.circular(8),
);
static const BorderRadius mediumBorderRadius = BorderRadius.all(
Radius.circular(10),
);
static const BorderRadius highBorderRadius = BorderRadius.all(
Radius.circular(16),
);
static final Border lightBorder = Border.all(color: DesignConfig.greyColor4);
static final BoxDecoration actionCardDecoration = BoxDecoration(
color: lightPrimaryColor3,
boxShadow: defaultShadow,
borderRadius: mediumBorderRadius,
);
static final List<BoxShadow> defaultShadow = [
BoxShadow(
color: const Color(0XFF4D4D4D).withOpacity(0.25),
offset: const Offset(0, 4),
blurRadius: 8,
spreadRadius: 0,
)
];
static const Duration defaultAppDuration = Duration(milliseconds: 400);
static const Duration defaultAnimationDuration = Duration(milliseconds: 600);
static final SystemUiOverlayStyle systemUIOverlayStyle = SystemUiOverlayStyle(
statusBarBrightness: Brightness.dark,

View File

@ -4,10 +4,21 @@ class Assets {
static const String _baseAnimationsPath = _basePath + '/animations';
static const String verticalLogoWithText =
_baseImagesPath + '/logo/logo-v-t.png';
_baseImagesPath + '/logos/logo-v-t.png';
static const String horizontalLogoWithText =
_baseImagesPath + '/logo/logo-h-t.png';
_baseImagesPath + '/logos/logo-h-t.png';
static const String logoLoadingAnimation =
_baseAnimationsPath + '/indicator.riv';
static const businessCategoryIcon =
_baseImagesPath + '/categories/business.png';
static const economicCategoryIcon =
_baseImagesPath + '/categories/economic.png';
static const enviromentalCategoryIcon =
_baseImagesPath + '/categories/enviromental.png';
static const politicalCategoryIcon =
_baseImagesPath + '/categories/political.png';
static const socialCategoryIcon = _baseImagesPath + '/categories/social.png';
static const techCategoryIcon = _baseImagesPath + '/categories/tech.png';
}

View File

@ -0,0 +1,7 @@
class RadarCategory {
final int id;
final String title;
final String asset;
RadarCategory({required this.id, required this.title, required this.asset});
}

View File

@ -27,7 +27,7 @@ class _AuthenticationState extends State<Authentication> {
return Scaffold(
body: Consumer<AuthenticationState>(
builder: (context, state, child) => AnimatedSwitcher(
duration: DesignConfig.defaultAppDuration,
duration: DesignConfig.defaultAnimationDuration,
child: _pages[state.currentPageIndex],
),
),

99
lib/pages/home/home.dart Normal file
View File

@ -0,0 +1,99 @@
import 'package:didvan/config/design_config.dart';
import 'package:didvan/pages/home/widgets/categories_gird.dart';
import 'package:didvan/pages/home/widgets/categories_list.dart';
import 'package:didvan/pages/home/widgets/search_field.dart';
import 'package:didvan/widgets/didvan/text.dart';
import 'package:didvan/widgets/logos/didvan_vertical_logo.dart';
import 'package:flutter/material.dart';
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
final ScrollController _scrollController = ScrollController();
bool _isColapsed = false;
bool _isAnimating = false;
@override
void initState() {
_scrollController.addListener(() async {
if (_isAnimating) return;
final double position = _scrollController.position.pixels;
if (position > 5 && !_isColapsed) {
_isColapsed = true;
setState(() {});
_isAnimating = true;
await _scrollController.animateTo(
380,
duration: DesignConfig.defaultAnimationDuration,
curve: Curves.ease,
);
_isAnimating = false;
} else if (position < 380 && _isColapsed) {
_isColapsed = false;
setState(() {});
_isAnimating = true;
await _scrollController.animateTo(
0,
duration: DesignConfig.defaultAnimationDuration,
curve: Curves.ease,
);
_isAnimating = false;
}
});
super.initState();
}
@override
Widget build(BuildContext context) {
final d = MediaQuery.of(context);
return Scaffold(
body: Stack(
children: [
CustomScrollView(
controller: _scrollController,
physics: const BouncingScrollPhysics(),
slivers: [
SliverPadding(
padding: const EdgeInsets.all(
20,
).copyWith(top: d.padding.top + 20),
sliver: SliverToBoxAdapter(
child: Container(
alignment: Alignment.centerRight,
height: 76,
child: const DidvanHorizontalLogo(),
),
),
),
const SliverPadding(
padding: EdgeInsets.symmetric(horizontal: 16),
sliver: SliverToBoxAdapter(
child: SearchField(),
),
),
SliverPadding(
padding: const EdgeInsets.only(top: 300, right: 16, bottom: 20),
sliver: SliverToBoxAdapter(
child: DidvanText(
'آخرین رصد',
style: Theme.of(context).textTheme.subtitle1,
color: DesignConfig.darkPrimaryColor2,
),
),
),
],
),
CategoriesRow1(isColapsed: _isColapsed),
CategoriesRow2(isColapsed: _isColapsed),
CategoriesList(isColapsed: _isColapsed),
],
),
);
}
}

View File

@ -0,0 +1,38 @@
import 'package:didvan/constants/assets.dart';
import 'package:didvan/models/radar_category.dart';
import 'package:didvan/providers/core_provider.dart';
class HomeState extends CoreProvier {
final List<RadarCategory> categories = [
RadarCategory(
id: 1,
title: 'افتصادی',
asset: Assets.economicCategoryIcon,
),
RadarCategory(
id: 2,
title: 'سیاسی',
asset: Assets.politicalCategoryIcon,
),
RadarCategory(
id: 3,
title: 'فناوری',
asset: Assets.techCategoryIcon,
),
RadarCategory(
id: 4,
title: 'کسب و کار',
asset: Assets.businessCategoryIcon,
),
RadarCategory(
id: 5,
title: 'زیست محیطی',
asset: Assets.enviromentalCategoryIcon,
),
RadarCategory(
id: 6,
title: 'اجتماعی',
asset: Assets.socialCategoryIcon,
),
];
}

View File

@ -0,0 +1,76 @@
import 'package:didvan/config/design_config.dart';
import 'package:didvan/pages/home/home_state.dart';
import 'package:didvan/pages/home/widgets/category_item.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class CategoriesRow1 extends StatelessWidget {
final bool isColapsed;
const CategoriesRow1({Key? key, required this.isColapsed}) : super(key: key);
@override
Widget build(BuildContext context) {
final MediaQueryData d = MediaQuery.of(context);
return AnimatedPositioned(
duration: DesignConfig.defaultAnimationDuration,
top: isColapsed ? 12 : 176 + d.padding.top,
left: isColapsed ? -d.size.width : 0,
right: isColapsed ? d.size.width : 0,
child: Row(
children: context
.read<HomeState>()
.categories
.sublist(0, 3)
.map(
(category) => Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 6),
child: CategoryItem(
title: category.title,
asset: category.asset,
isColapsed: isColapsed,
),
),
),
)
.toList(),
),
);
}
}
class CategoriesRow2 extends StatelessWidget {
const CategoriesRow2({
Key? key,
required this.isColapsed,
}) : super(key: key);
final bool isColapsed;
@override
Widget build(BuildContext context) {
final MediaQueryData d = MediaQuery.of(context);
return AnimatedPositioned(
duration: DesignConfig.defaultAnimationDuration,
top: isColapsed ? -60 : 300 + d.padding.top,
left: isColapsed ? -80 : 0,
right: isColapsed ? 124 : 0,
child: Row(
children: context
.read<HomeState>()
.categories
.sublist(3, 6)
.map(
(category) => Expanded(
child: CategoryItem(
title: category.title,
asset: category.asset,
isColapsed: isColapsed,
),
),
)
.toList(),
),
);
}
}

View File

@ -0,0 +1,76 @@
import 'package:didvan/config/design_config.dart';
import 'package:didvan/models/radar_category.dart';
import 'package:didvan/pages/home/home_state.dart';
import 'package:didvan/widgets/animated_visibility.dart';
import 'package:didvan/widgets/didvan/text.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class CategoriesList extends StatelessWidget {
final bool isColapsed;
const CategoriesList({Key? key, required this.isColapsed}) : super(key: key);
@override
Widget build(BuildContext context) {
final MediaQueryData d = MediaQuery.of(context);
final HomeState state = context.read<HomeState>();
final List<RadarCategory> categories = [];
categories.addAll(state.categories.sublist(3, 6));
categories.addAll(state.categories.sublist(0, 3));
return Positioned(
top: 0,
left: 0,
right: 0,
child: AnimatedCrossFade(
crossFadeState:
isColapsed ? CrossFadeState.showSecond : CrossFadeState.showFirst,
duration: DesignConfig.defaultAnimationDuration,
firstChild: const SizedBox(),
secondChild: Container(
height: 60 + d.padding.top,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
boxShadow: DesignConfig.defaultShadow,
),
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
scrollDirection: Axis.horizontal,
padding: EdgeInsets.only(
top: d.padding.top + 12,
bottom: 12,
right: 12,
),
child: Row(
children: [
AnimatedVisibility(
isVisible: isColapsed,
duration: DesignConfig.defaultAnimationDuration,
child: _itemBuilder(
RadarCategory(title: 'همه', asset: '', id: 0),
),
),
for (var category in categories) _itemBuilder(category),
],
),
),
),
),
);
}
Widget _itemBuilder(RadarCategory category) {
return Container(
margin: const EdgeInsets.only(left: 12),
width: 100,
padding: const EdgeInsets.all(4),
alignment: Alignment.center,
child: DidvanText(category.title),
decoration: BoxDecoration(
border: Border.all(
color: DesignConfig.darkPrimaryColor2,
),
borderRadius: DesignConfig.lowBorderRadius,
),
);
}
}

View File

@ -0,0 +1,61 @@
import 'package:didvan/config/design_config.dart';
import 'package:didvan/widgets/animated_visibility.dart';
import 'package:didvan/widgets/didvan/text.dart';
import 'package:flutter/material.dart';
class CategoryItem extends StatelessWidget {
final String title;
final String asset;
final bool isColapsed;
const CategoryItem({
Key? key,
required this.title,
required this.asset,
required this.isColapsed,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final Size ds = MediaQuery.of(context).size;
return Center(
child: AnimatedContainer(
duration: DesignConfig.defaultAnimationDuration,
padding: isColapsed ? const EdgeInsets.all(4) : EdgeInsets.zero,
width: isColapsed ? 100 : ds.width / 3,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: DesignConfig.lowBorderRadius,
border: isColapsed
? Border.all(color: DesignConfig.darkPrimaryColor2)
: null,
),
child: Column(
children: [
AnimatedVisibility(
duration: DesignConfig.defaultAnimationDuration,
isVisible: !isColapsed,
child: Container(
width: ds.width / 5,
height: ds.width / 5,
decoration: DesignConfig.actionCardDecoration,
padding: const EdgeInsets.all(8),
child: Image.asset(
asset,
),
),
),
const SizedBox(
height: 8,
),
DidvanText(
title,
style: Theme.of(context).textTheme.subtitle2,
color: DesignConfig.darkPrimaryColor2,
),
],
),
),
);
}
}

View File

@ -0,0 +1,47 @@
import 'package:didvan/config/design_config.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:flutter/material.dart';
class SearchField extends StatelessWidget {
const SearchField({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
height: 40,
width: double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
),
child: Row(
children: [
const Icon(
DidvanIcons.search_regular,
),
Expanded(
child: TextField(
style: Theme.of(context).textTheme.bodyText1,
textAlignVertical: TextAlignVertical.top,
onChanged: (value) {},
keyboardType: TextInputType.text,
textInputAction: TextInputAction.search,
decoration: InputDecoration(
contentPadding: const EdgeInsets.only(
left: 12,
right: 12,
bottom: 8,
),
border: InputBorder.none,
hintText: 'جستجو مطلب در رادار',
hintStyle: Theme.of(context)
.textTheme
.subtitle2!
.copyWith(color: DesignConfig.greyColor5),
),
),
),
],
),
);
}
}

View File

@ -16,7 +16,7 @@ class _SplashState extends State<Splash> {
Future.delayed(
const Duration(seconds: 2),
() => Navigator.of(context).pushReplacementNamed(
Routes.authenticaion,
Routes.home,
),
);
super.initState();

View File

@ -1,5 +1,7 @@
import 'package:didvan/pages/authentication/authentication.dart';
import 'package:didvan/pages/authentication/authentication_state.dart';
import 'package:didvan/pages/home/home.dart';
import 'package:didvan/pages/home/home_state.dart';
import 'package:didvan/pages/splash/splash.dart';
import 'package:didvan/pages/splash/splash_state.dart';
import 'package:didvan/routes/routes.dart';
@ -11,18 +13,25 @@ class RouteGenerator {
switch (settings.name) {
case Routes.splash:
return _materialPageRouteGenerator(
ChangeNotifierProvider(
ChangeNotifierProvider<SplashState>(
create: (context) => SplashState(),
child: const Splash(),
),
);
case Routes.authenticaion:
return _materialPageRouteGenerator(
ChangeNotifierProvider(
ChangeNotifierProvider<AuthenticationState>(
create: (context) => AuthenticationState(),
child: const Authentication(),
),
);
case Routes.home:
return _materialPageRouteGenerator(
ChangeNotifierProvider<HomeState>(
create: (context) => HomeState(),
child: const Home(),
),
);
default:
return _errorRoute();
}

View File

@ -0,0 +1,90 @@
library animated_visibility;
import 'package:flutter/material.dart';
enum FadeMode {
vertical,
horizontal,
both,
}
class AnimatedVisibility extends StatefulWidget {
final bool isVisible;
final Duration duration;
final Widget child;
final Curve curve;
final FadeMode fadeMode;
const AnimatedVisibility({
Key? key,
required this.isVisible,
required this.duration,
required this.child,
this.fadeMode = FadeMode.both,
this.curve = Curves.easeIn,
}) : super(key: key);
@override
_AnimatedVisibilityState createState() => _AnimatedVisibilityState();
}
class _AnimatedVisibilityState extends State<AnimatedVisibility>
with SingleTickerProviderStateMixin {
late final AnimationController _sizeController;
late final Animation<double> _animation;
@override
void initState() {
super.initState();
_setupAnimation();
_runSizeCheck();
}
void _setupAnimation() {
_sizeController =
AnimationController(vsync: this, duration: widget.duration);
_animation = CurvedAnimation(
parent: _sizeController,
curve: widget.curve,
);
}
@override
void didUpdateWidget(covariant AnimatedVisibility oldWidget) {
_runSizeCheck();
super.didUpdateWidget(oldWidget);
}
void _runSizeCheck() {
if (widget.isVisible) {
_sizeController.forward();
} else {
_sizeController.reverse();
}
}
@override
void dispose() {
_sizeController.dispose();
super.dispose();
}
bool get _isVertical =>
widget.fadeMode == FadeMode.vertical || widget.fadeMode == FadeMode.both;
bool get _isHorizontal =>
widget.fadeMode == FadeMode.horizontal ||
widget.fadeMode == FadeMode.both;
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animation,
child: widget.child,
builder: (context, child) => Align(
heightFactor: _isVertical ? _animation.value : null,
widthFactor: _isHorizontal ? _animation.value : null,
child: Opacity(opacity: _animation.value, child: child),
),
);
}
}

View File

@ -7,6 +7,7 @@ class DidvanText extends StatelessWidget {
final Color? color;
final FontWeight? fontWeight;
final double? fontSize;
final TextAlign textAlign;
const DidvanText(
this.text, {
@ -15,6 +16,7 @@ class DidvanText extends StatelessWidget {
this.color,
this.fontSize,
this.fontWeight,
this.textAlign = TextAlign.right,
}) : super(key: key);
@override
@ -26,6 +28,7 @@ class DidvanText extends StatelessWidget {
fontWeight: fontWeight,
fontSize: fontSize,
),
textAlign: textAlign,
);
}
}

View File

@ -133,5 +133,7 @@ class _DidvanTextFieldState extends State<DidvanTextField> {
}
}
String? _validator(String? value) {}
String? _validator(String? value) {
_hasError = false;
}
}

View File

@ -0,0 +1,38 @@
import 'package:flutter/material.dart';
class InkWrapper extends StatelessWidget {
final Color? splashColor;
final Color? highlightColor;
final Widget child;
final VoidCallback? onPressed;
final BorderRadius? borderRadius;
const InkWrapper({
Key? key,
this.splashColor,
this.highlightColor,
required this.child,
this.onPressed,
this.borderRadius,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
child,
Positioned.fill(
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: borderRadius,
splashColor: splashColor,
highlightColor: highlightColor,
onTap: onPressed,
),
),
),
],
);
}
}

View File

@ -0,0 +1,11 @@
import 'package:didvan/constants/assets.dart';
import 'package:flutter/material.dart';
class DidvanHorizontalLogo extends StatelessWidget {
const DidvanHorizontalLogo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Image.asset(Assets.horizontalLogoWithText);
}
}

View File

@ -64,8 +64,15 @@ flutter:
# To add assets to your application, add an assets section, like this:
assets:
- lib/assets/images/logo/logo-v-t.png
- lib/assets/images/logo/logo-h-t.png
- lib/assets/images/logos/logo-v-t.png
- lib/assets/images/logos/logo-h-t.png
- lib/assets/images/categories/business.png
- lib/assets/images/categories/economic.png
- lib/assets/images/categories/enviromental.png
- lib/assets/images/categories/political.png
- lib/assets/images/categories/social.png
- lib/assets/images/categories/tech.png
- lib/assets/animations/indicator.riv
- lib/assets/animations/indicator.riv