123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576 |
- import 'dart:math';
- import 'dart:ui';
- import 'package:flutter/cupertino.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter/rendering.dart';
- import 'package:flutter/services.dart';
- import 'package:flutter_easyrefresh/easy_refresh.dart';
- import 'package:provider/provider.dart';
- import 'package:shared_preferences/shared_preferences.dart';
- import 'package:sport/bean/forum.dart';
- import 'package:sport/bean/post.dart';
- import 'package:sport/pages/social/post_page.dart';
- import 'package:sport/pages/social/post_widget.dart';
- import 'package:sport/pages/social/search_page.dart';
- import 'package:sport/provider/lib/provider_widget.dart';
- import 'package:sport/provider/lib/view_state.dart';
- import 'package:sport/provider/lib/view_state_lifecycle.dart';
- import 'package:sport/provider/social_detail_model.dart';
- import 'package:sport/provider/social_index_model.dart';
- import 'package:sport/router/navigator_util.dart';
- import 'package:sport/router/routes.dart';
- import 'package:sport/services/api/inject_api.dart';
- import 'package:sport/services/api/resp.dart';
- import 'package:sport/services/userid.dart';
- import 'package:sport/widgets/appbar.dart';
- import 'package:sport/widgets/error.dart';
- import 'package:sport/widgets/loading.dart';
- import 'package:sport/widgets/misc.dart';
- import 'package:sport/widgets/persistent_header.dart';
- import 'package:sport/widgets/space.dart';
- final List<String> _tabs = ['热门', '关注', '最新', '精华', '官方'];
- class NewSocialIndexPage extends StatefulWidget {
- NewSocialIndexPage();
- @override
- State<StatefulWidget> createState() => _PageState();
- }
- class _PageState extends ViewStateLifecycle<NewSocialIndexPage, SocialDetailModel> with TickerProviderStateMixin, UserId, InjectApi {
- ScrollController _controller;
- double _expandedHeight = 0;
- int _brightness = 0;
- TabController _tabController;
- Future<RespPage<Post>> _getPostListByOfficial;
- final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
- @override
- SocialDetailModel createModel() => SocialDetailModel(0);
- SocialIndexModel indexModel = new SocialIndexModel();
- // 右边draw中的 当前选择的索引...
- ValueNotifier<int> drawOneIndex = ValueNotifier(0);
- ValueNotifier<int> drawTwoIndex = ValueNotifier(0);
- String forumId = "";
- String isOfficial;
- List buttonList = [];
- List<Forum> buttonData = [
- new Forum(
- gameName: "全部",
- ),
- new Forum(
- gameName: "用户发布",
- forumId: "0",
- ),
- new Forum(gameName: "官方发布", forumId: "1"),
- ];
- ValueNotifier<bool> isShowSelect = ValueNotifier(false);
- void _refresh() {
- model?.setForumIdAndOrigin(_tabController.index, forumId, isOfficial);
- _controller.animateTo(0, duration: Duration(milliseconds: 100), curve: Curves.ease);
- }
- @override
- void initState() {
- super.initState();
- _tabController = TabController(length: _tabs.length, initialIndex: 0, vsync: this)
- ..addListener(() {
- if (_tabController.index.toDouble() == _tabController.animation.value) {
- _refresh();
- }
- });
- _controller = ScrollController()
- ..addListener(() {
- if (_controller.position.pixels >= _expandedHeight - 70) {
- if (_brightness == 0) {
- setState(() {
- _brightness = 1;
- });
- }
- } else {
- if (_brightness == 1) {
- setState(() {
- _brightness = 0;
- });
- }
- }
- });
- initButtonList();
- }
- @override
- void dispose() {
- super.dispose();
- _controller?.dispose();
- _tabController?.dispose();
- PaintingBinding.instance.imageCache.clear();
- }
- Future<int> _getCount() async {
- int count = 0;
- try {
- count += (await model.api.getNoticeCount("comment", "0")).data;
- } catch (e) {
- print(e);
- }
- if (count == 0) {
- try {
- count += (await model.api.getNoticeCount("like", "0")).data;
- } catch (e) {
- print(e);
- }
- }
- return count;
- }
- Widget _buildSearchWidget() {
- return GestureDetector(
- onTap: () {
- Navigator.push(context, new MaterialPageRoute(builder: (context) => SearchPage()));
- },
- child: Container(
- // margin: EdgeInsets.fromLTRB(12.0, 0, 12.0, 12.0),
- width: MediaQuery.of(context).size.width * 0.8,
- height: 35,
- padding: EdgeInsets.fromLTRB(12.0, 0, 12.0, 0),
- decoration: BoxDecoration(
- color: Color(0xffF1F1F1),
- shape: BoxShape.rectangle,
- borderRadius: BorderRadius.all(Radius.circular(50)),
- ),
- child: Row(
- children: <Widget>[
- Image.asset("lib/assets/img/searchbar_icon_search.png"),
- Space(
- width: 4,
- ),
- Text(
- "输入关键词",
- strutStyle: StrutStyle(forceStrutHeight: true),
- style: TextStyle(fontSize: 14, color: Color(0xff999999)),
- ),
- ],
- ),
- ),
- );
- }
- // @title button 的title
- // @index 当前显示的下标
- // @targetIndex 目标下标
- // @type origin / project
- Widget _buildDrawerButtonItem(Forum data, int index, ValueNotifier<int> targetIndex, String type) {
- return InkWell(
- child: Container(
- // height: 35.0,
- decoration: BoxDecoration(
- color: index == targetIndex.value ? Theme.of(context).accentColor : Colors.white,
- borderRadius: BorderRadius.all(Radius.circular(20.0)),
- border: Border.all(color: index == targetIndex.value ? Colors.white : Theme.of(context).dividerTheme.color)),
- padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 25.0),
- child: Text(
- data.gameName,
- strutStyle: StrutStyle(forceStrutHeight: true),
- style: TextStyle(fontSize: 14.0, color: index == targetIndex.value ? Colors.white : Color(0xff999999)),
- ),
- ),
- onTap: () {
- targetIndex.value = index;
- if (type == "project") {
- forumId = data.forumId;
- } else if (type == "origin") {
- isOfficial = data.forumId;
- }
- if (drawOneIndex.value == 0 && drawTwoIndex.value == 0) {
- isShowSelect.value = false;
- } else {
- isShowSelect.value = true;
- }
- model.setForumIdAndOrigin(_tabController.index, forumId, isOfficial);
- },
- );
- }
- Widget _buildDrawButtonContainer(Widget buttons, {String type = 'top'}) {
- return Padding(
- padding: EdgeInsets.only(left: 16.0),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: <Widget>[
- type == 'top'
- ? Space(
- height: 21.0,
- )
- : Container(),
- type == 'bottom'
- ? Space(
- height: 43.0,
- )
- : Container(),
- Text("运动项目", style: TextStyle(color: Color(0xff333333), fontSize: 16.0, fontWeight: FontWeight.bold)),
- Space(
- height: 16.0,
- ),
- buttons,
- type == 'bottom'
- ? Space(
- height: 21.0,
- )
- : Container(),
- ],
- ),
- );
- }
- Widget _buildButtons(ValueNotifier<int> targetIndex, {List<dynamic> list}) {
- return ValueListenableBuilder(
- valueListenable: targetIndex,
- builder: (BuildContext context, int value, Widget child) => Wrap(
- alignment: WrapAlignment.start,
- crossAxisAlignment: WrapCrossAlignment.start,
- spacing: 8.0,
- runSpacing: 16.0,
- children: list != null
- ? list.asMap().entries.map((e) => _buildDrawerButtonItem(e.value, e.key, targetIndex, "project")).toList()
- : buttonData.asMap().entries.map((e) => _buildDrawerButtonItem(e.value, e.key, targetIndex, "origin")).toList(),
- ),
- );
- }
- Widget _buildEndrawer() {
- return Container(
- height: double.infinity,
- width: MediaQuery.of(context).size.width * 0.65,
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.only(
- topLeft: Radius.circular(10.0),
- bottomLeft: Radius.circular(10.0),
- ),
- ),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: <Widget>[
- _buildDrawButtonContainer(
- _buildButtons(drawOneIndex, list: buttonList),
- type: "bottom",
- ),
- Divider(),
- _buildDrawButtonContainer(
- _buildButtons(drawTwoIndex),
- type: "top",
- ),
- ],
- ));
- }
- Widget _buildFilterButton(String name, ValueNotifier<int> index, String type) {
- return InkWell(
- child: Container(
- padding: EdgeInsets.symmetric(vertical: 6.0, horizontal: 16.0),
- decoration: BoxDecoration(border: Border.all(color: Theme.of(context).accentColor), borderRadius: BorderRadius.all(Radius.circular(44.0))),
- child: Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: <Widget>[
- Text(
- "$name",
- strutStyle: StrutStyle(forceStrutHeight: true),
- style: TextStyle(color: Theme.of(context).accentColor, fontSize: 12.0),
- ),
- Space(
- width: 5.0,
- ),
- Image.asset(
- "lib/assets/img/btn_close_yellow.png",
- width: 7.0,
- height: 7.0,
- )
- ],
- ),
- ),
- onTap: () {
- type == "one" ? model.setForumIdAndOrigin(_tabController.index, "", isOfficial) : model.setForumIdAndOrigin(_tabController.index, forumId, "");
- index.value = 0;
- },
- );
- }
- initButtonList() async {
- RespList<Forum> data = await api.getForumIndex();
- setState(() {
- buttonList = data.results;
- if (data.results.length > 0) {
- buttonList.insert(0, new Forum(gameName: "全部"));
- }
- });
- }
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- key: _scaffoldKey,
- backgroundColor: Colors.white,
- endDrawer: _buildEndrawer(),
- body: ProviderWidget2<SocialDetailModel, SocialIndexModel>(
- model1: model,
- model2: indexModel,
- onModelReady: (model1, model2) => model.initData(),
- builder: (_, model, model2, __) {
- return EasyRefresh.builder(
- controller: model.refreshController,
- enableControlFinishRefresh: true,
- enableControlFinishLoad: true,
- onRefresh: () => model.refresh(),
- onLoad: model.isIdle ? () => model.loadMore() : null,
- header: buildClassicalHeader(),
- footer: buildClassicalFooter(),
- builder: (context, physics, header, footer) {
- return AnnotatedRegion<SystemUiOverlayStyle>(
- value: SystemUiOverlayStyle.dark,
- child: Material(
- color: Colors.white,
- child: SafeArea(
- child: CustomScrollView(
- controller: _controller,
- physics: physics,
- slivers: <Widget>[
- buildSliverAppBar(context, "社区",
- canBack: false,
- pinned: false,
- height: 100.0,
- padding: const EdgeInsets.fromLTRB(12.0, 0, 0, 6.0),
- actions: <Widget>[
- Selector<SocialIndexModel, ViewState>(
- selector: (_, SocialIndexModel model) => model.viewState,
- shouldRebuild: (_, v) => v == ViewState.idle,
- builder: (BuildContext context, ViewState value, Widget child) {
- return IconButton(
- icon: Stack(
- alignment: Alignment.center,
- children: <Widget>[
- Image.asset("lib/assets/img/bbs_icon_news.png"),
- FutureBuilder(
- future: SharedPreferences.getInstance(),
- builder: (BuildContext context, AsyncSnapshot<SharedPreferences> snapshot) {
- if (snapshot.connectionState == ConnectionState.done) {
- if (snapshot.data.getBool("message_setting") ?? true == true) {
- return FutureBuilder(
- future: _getCount(),
- builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
- if (snapshot.data != null && snapshot.data > 0) {
- return Align(
- alignment: Alignment.topRight,
- child: Container(
- margin: const EdgeInsets.only(top: 6.0),
- width: 10,
- height: 10,
- decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.red),
- child: Center(
- child: Text(""),
- ),
- ),
- );
- }
- return Container();
- });
- }
- }
- return Container();
- }),
- ],
- ),
- onPressed: () async {
- await NavigatorUtil.go(context, Routes.socialMessage);
- setState(() {});
- },
- );
- }),
- ],
- paddingLeading: false),
- header,
- SliverPersistentHeader(
- delegate: PersistentHeader(
- min: 50,
- max: 50,
- child: Container(
- color: Colors.white,
- padding: EdgeInsets.only(bottom: 10),
- child: TabBar(
- isScrollable: true,
- indicatorPadding: EdgeInsets.symmetric(horizontal: 8),
- indicatorWeight: 3,
- controller: _tabController,
- tabs: _tabs.map((e) => Tab(text: e)).toList(),
- ),
- )),
- pinned: true,
- ),
- SliverToBoxAdapter(
- child: Padding(
- padding: EdgeInsets.symmetric(vertical: 6.0, horizontal: 12.0),
- child: Row(
- children: <Widget>[
- _buildSearchWidget(),
- Space(
- width: 15.0,
- ),
- ValueListenableBuilder(
- valueListenable: isShowSelect,
- builder: (context, flag, child) => InkWell(
- child: flag
- ? Image.asset(
- "lib/assets/img/bbs_icon_choose_press.png",
- width: 22,
- height: 22,
- )
- : Image.asset(
- "lib/assets/img/bbs_icon_choose_normal.png",
- width: 22,
- height: 22,
- ),
- onTap: () => _scaffoldKey.currentState.openEndDrawer(),
- ),
- ),
- ],
- ),
- ),
- ),
- SliverToBoxAdapter(
- child: Padding(
- padding: EdgeInsets.only(left: 12.0),
- child: Row(
- children: <Widget>[
- if (drawOneIndex.value != 0) _buildFilterButton('${buttonList[drawOneIndex.value].gameName}', drawOneIndex, "one"),
- Space(
- width: 10.0,
- ),
- if (drawTwoIndex.value != 0) _buildFilterButton('${buttonData[drawTwoIndex.value].gameName}', drawTwoIndex, "two"),
- ],
- ),
- ),
- ),
- if (model.isBusy)
- SliverToBoxAdapter(
- child: RequestLoadingWidget(),
- ),
- if (model.isEmpty)
- SliverFillRemaining(
- child: Center(
- child: RequestErrorWidget(
- null,
- msg: "暂无帖子~",
- assets: RequestErrorWidget.ASSETS_NO_INVITATION,
- ),
- ),
- ),
- if (model.isIdle)
- SliverList(
- delegate: SliverChildBuilderDelegate(
- (context, index) {
- Post post = model.list[index];
- return PostWidget(post, model, selfId == post.userId);
- },
- childCount: model.list.length,
- ),
- ),
- ],
- ),
- ),
- ),
- );
- });
- },
- ),
- floatingActionButtonLocation: const _EndFloatFloatingActionButtonLocation(),
- floatingActionButtonAnimator:const _ScalingFabMotionAnimator(),
- floatingActionButton: InkWell(
- child: Image.asset("lib/assets/img/bbs_icon_edit.png"),
- onTap: () async {
- // print('FloatingActionButton');
- var result = await NavigatorUtil.goPage(
- context,
- (context) => PostPage(
- "",
- forums: buttonList,
- ));
- if (result == true) {
- if (_tabController.index.toDouble() == _tabController.animation.value) {
- _refresh();
- } else {
- _tabController?.index = 2;
- }
- }
- },
- ),
- );
- }
- }
- class _EndFloatFloatingActionButtonLocation extends FloatingActionButtonLocation {
- const _EndFloatFloatingActionButtonLocation();
- double _rightOffset(ScaffoldPrelayoutGeometry scaffoldGeometry, {double offset = 0.0}) {
- return scaffoldGeometry.scaffoldSize.width - scaffoldGeometry.minInsets.right - scaffoldGeometry.floatingActionButtonSize.width + offset;
- }
- double getDockedY(ScaffoldPrelayoutGeometry scaffoldGeometry) {
- final double contentBottom = scaffoldGeometry.contentBottom;
- final double bottomSheetHeight = scaffoldGeometry.bottomSheetSize.height;
- final double fabHeight = scaffoldGeometry.floatingActionButtonSize.height;
- final double snackBarHeight = scaffoldGeometry.snackBarSize.height;
- double fabY = contentBottom - fabHeight / 2.0;
- // The FAB should sit with a margin between it and the snack bar.
- if (snackBarHeight > 0.0) fabY = min(fabY, contentBottom - snackBarHeight - fabHeight - kFloatingActionButtonMargin);
- // The FAB should sit with its center in front of the top of the bottom sheet.
- if (bottomSheetHeight > 0.0) fabY = min(fabY, contentBottom - bottomSheetHeight - fabHeight / 2.0);
- final double maxFabY = scaffoldGeometry.scaffoldSize.height - fabHeight;
- return min(maxFabY, fabY);
- }
- @override
- Offset getOffset(ScaffoldPrelayoutGeometry scaffoldGeometry) {
- final double fabX = _rightOffset(scaffoldGeometry, offset: -8.0);
- final double fabY = getDockedY(scaffoldGeometry);
- return Offset(fabX, fabY - 8);
- }
- @override
- String toString() => 'FloatingActionButtonLocation.endFloat';
- }
- class _ScalingFabMotionAnimator extends FloatingActionButtonAnimator {
- const _ScalingFabMotionAnimator();
- @override
- Offset getOffset({ Offset begin, Offset end, double progress }) {
- return end;
- }
- @override
- Animation<double> getScaleAnimation({ Animation<double> parent }) {
- return AlwaysStoppedAnimation(1.0);
- }
- @override
- Animation<double> getRotationAnimation({ Animation<double> parent }) {
- return AlwaysStoppedAnimation(1.0);
- }
- // If the animation was just starting, we'll continue from where we left off.
- // If the animation was finishing, we'll treat it as if we were starting at that point in reverse.
- // This avoids a size jump during the animation.
- @override
- double getAnimationRestart(double previousValue) => 1.0;
- }
|