Merge pull request #201 from alibaba/dev/yisheng-v2

modfiy: 业内资讯的升级
This commit is contained in:
ryan730
2019-05-01 00:44:45 +08:00
committed by GitHub
25 changed files with 1448 additions and 22 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
flutter_01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View File

@ -0,0 +1,35 @@
/**
* Created with Android Studio.
* User: 一晟
* Date: 2019/4/28
* Time: 3:20 PM
* email: zhu.yan@alibaba-inc.com
* tartget: FlatButton 的示例
*/
import 'dart:async';
import 'package:dio/dio.dart';
import 'dart:convert';
import './search_result.dart';
import 'package:html/parser.dart' show parse;
var dio = new Dio();
class Api {
Future<List<SearchResult>> search(name) async {
print('=========>>>');
var response = await dio.get("https://www.so.com/s?ie=utf-8&q=$name");
// var document = parse(response.data);
// var app = document.querySelectorAll('.res-title a');
List<SearchResult> res = [];
// app.forEach((f) {
// res.add(
// SearchResult(
// title: f.text,
// source: f.attributes["data-url"] ?? f.attributes["href"],
// ),
// );
// });
return res;
}
}
Api api = Api();

View File

@ -0,0 +1,35 @@
/**
* Created with Android Studio.
* User: 一晟
* Date: 2019/4/28
* Time: 7:17 PM
* email: zhu.yan@alibaba-inc.com
* tartget:
*/
import 'dart:async';
import 'package:bloc/bloc.dart';
import './search_event.dart';
import './search_state.dart';
import './search_api.dart';
/// 这里导入api类与上面的SearchEvent与SearchState文件
class SearchBloc extends Bloc<SearchEvent, SearchState> {
@override
SearchState get initialState => SearchUninitialized();
@override
Stream<SearchState> mapEventToState(SearchEvent event,) async* {
if (event is SearchFetch) {
try {
yield SearchLoading();
final res = await api.search(event.query);
yield SearchLoaded(res: res);
} catch (_) {
yield SearchError();
}
}
}
}

View File

@ -0,0 +1,18 @@
/**
* Created with Android Studio.
* User: 一晟
* Date: 2019/4/28
* Time: 7:18 PM
* email: zhu.yan@alibaba-inc.com
* tartget:
*/
abstract class SearchEvent {}
class SearchFetch extends SearchEvent {
final String query;
SearchFetch({this.query});
@override
String toString() => 'SearchFetch获取搜索结果事件';
}

View File

@ -0,0 +1,14 @@
/**
* Created with Android Studio.
* User: 一晟
* Date: 2019/4/28
* Time: 7:11 PM
* email: zhu.yan@alibaba-inc.com
* tartget:
*/
class SearchResult {
String title;
String source;
SearchResult({this.title, this.source});
}

View File

@ -0,0 +1,37 @@
/**
* Created with Android Studio.
* User: 一晟
* Date: 2019/4/28
* Time: 7:13 PM
* email: zhu.yan@alibaba-inc.com
* tartget:
*/
import './search_result.dart';
abstract class SearchState {}
class SearchError extends SearchState {
@override
String toString() => 'SearchError获取失败';
}
class SearchUninitialized extends SearchState {
@override
String toString() => 'SearchUninitialized未初始化';
}
class SearchLoading extends SearchState {
@override
String toString() => 'SearchLoading :正在加载';
}
class SearchLoaded extends SearchState {
final List<SearchResult> res;
SearchLoaded({
this.res,
});
@override
String toString() => 'SearchLoaded加载完毕';
}

View File

@ -0,0 +1,80 @@
/**
* Created with Android Studio.
* User: 一晟
* Date: 2019/4/28
* Time: 7:19 PM
* email: zhu.yan@alibaba-inc.com
* tartget:
*/
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
//import 'package:suiyi/blocs/search/bloc.dart';
import './search_event.dart';
import './search_state.dart';
import './search_bloc.dart';
class SearchWidget extends StatefulWidget {
final SearchDelegate delegate;
final String query;
SearchWidget({this.delegate, this.query});
@override
_SearchWidgetState createState() => _SearchWidgetState();
}
class _SearchWidgetState extends State<SearchWidget> {
final SearchBloc _search = SearchBloc();
String old;
@override
void dispose() {
_search.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
print('1:${old}');
print('2:${widget.query}');
if (old != widget.query) {
_search.dispatch(SearchFetch(query: widget.query));
old = widget.query;
}
return BlocBuilder(
bloc: _search,
builder: (BuildContext context, SearchState state) {
print('-------${state}');
if (state is SearchUninitialized || state is SearchLoading) {
return Center(
child: CircularProgressIndicator(),
);
} else if (state is SearchError) {
return Center(
child: Text('获取失败'),
);
} else if (state is SearchLoaded) {
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
return ListTile(
dense: true,
leading: Icon(
Icons.bookmark_border,
size: 32,
),
title: Text(
state.res[index].title,
overflow: TextOverflow.ellipsis,
),
subtitle: Text(state.res[index].source),
onTap: () {
// 在这里对选中的结果进行解析因为我目前是用golang实现的所以就没贴代码了。
print(state.res[index].source);
},
);
},
itemCount: state.res.length,
);
}
},
);
}
}

View File

@ -0,0 +1,71 @@
/**
* Created with Android Studio.
* User: 一晟
* Date: 2019/4/28
* Time: 3:20 PM
* email: zhu.yan@alibaba-inc.com
* tartget: FlatButton 的示例
*/
import 'dart:async';
import 'package:dio/dio.dart';
import 'dart:convert';
import 'package:html/parser.dart' show parse;
import './industry_model.dart';
import './search_result.dart';
var dio = new Dio();
//class Api2 {
// /// 关键字提示(起点)
// Future<List<String>> suggestion(String query) async {
//// http.Response response = await http.get(
//// "https://www.qidian.com/ajax/Search/AutoComplete?siteid=1&query=$query");
// var response = await dio.get("https://www.qidian.com/ajax/Search/AutoComplete?siteid=1&query=$query", data: {});
// //var response = await dio.get("https://www.so.com/s?ie=utf-8&q=$query");
// print('1=====>${query}');
// print('2=====>${response.data}');
// //var data = Suggestion.fromJson(json.decode(response.body));
// //var data = Suggestion.fromJson(json.decode(response.data));
// var data = Suggestion.fromJson(json.decode(response.data));
// List<String> suggestion = [];
// data.suggestions.forEach((k) {
// //print('=====>${k.value}');
// suggestion.add(k.value);
// });
//
// return Future.delayed(Duration(seconds:2), () {
// return suggestion;
// });
// //return suggestion;
// }
//}
class Api {
/// 关键字提示(起点)
Future<List<SearchResult>> suggestion(String query) async {
// http.Response response = await http.get(
// "https://www.qidian.com/ajax/Search/AutoComplete?siteid=1&query=$query");
/// var response = await dio.get("https://www.qidian.com/ajax/Search/AutoComplete?siteid=1&query=$query", data: {});
var response = await dio.get("https://www.so.com/s?ie=utf-8&q=$query flutter");
var document = parse(response.data);
var app = document.querySelectorAll('.res-title a');
///print('1=====>${query}');
///print('2=====>${response.data}');
////print('3=====>${app}');
List<SearchResult> res = [];
app.forEach((f) {
///print('f==>${f}');
res.add(
SearchResult(
title: f.text,
source: f.attributes["data-url"] ?? f.attributes["href"],
),
);
});
return Future.delayed(Duration(seconds:2), () {
return res;
});
//return suggestion;
}
}
Api api = Api();

View File

@ -0,0 +1,41 @@
/**
* Created with Android Studio.
* User: 一晟
* Date: 2019/4/28
* Time: 5:19 PM
* email: zhu.yan@alibaba-inc.com
* tartget:
*/
import 'dart:async';
import 'package:bloc/bloc.dart';
import './industry_api.dart';
import './industry_event.dart';
import './industry_state.dart';
class SuggestionBloc extends Bloc<SuggestionEvent, SuggestionState> {
@override
SuggestionState get initialState => SuggestionUninitialized();
@override
Stream<SuggestionState> mapEventToState(SuggestionEvent event)async* {
//Stream<SuggestionState> mapEventToState(SuggestionState currentState, SuggestionEvent event,) async* {
if (event is SuggestionFetch) {
//print('event==>${event}');
try {
yield SuggestionLoading();
final res = await api.suggestion(event.query);
print('res====>${res}');
yield SuggestionLoaded(res: res);
} catch (_) {
yield SuggestionError();
}
}
if (event is SuggestionClearFetch) {
//print('event==>${event}');
try {
yield SuggestionUninitialized();
} catch (_) {
yield SuggestionError();
}
}
}
}

View File

@ -0,0 +1,26 @@
/**
* Created with Android Studio.
* User: 一晟
* Date: 2019/4/28
* Time: 3:35 PM
* email: zhu.yan@alibaba-inc.com
*/
abstract class SuggestionEvent {}
class SuggestionFetch extends SuggestionEvent {
final String query;
SuggestionFetch({this.query});
@override
String toString() => 'SuggestionFetch:获取关键字提示事件';
}
class SuggestionClearFetch extends SuggestionEvent {
final String query;
SuggestionClearFetch({this.query});
@override
String toString() => 'SuggestionClearFetch:清空界面';
}

View File

@ -0,0 +1,79 @@
/**
* Created with Android Studio.
* User: 一晟
* Date: 2019/4/28
* Time: 3:52 PM
* email: zhu.yan@alibaba-inc.com
*/
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import './industry_bloc.dart';
import './industry_event.dart';
import './industry_state.dart';
final SuggestionBloc suggestion = SuggestionBloc();
class IndustryPage extends StatefulWidget {
final Function itemTitle;
IndustryPage({Key key,this.itemTitle}) : super(key: key);
@override
_IndustryState createState() => _IndustryState();
}
class _IndustryState extends State<IndustryPage> {
@override
Widget build(BuildContext context) {
return Material(
child: Column(
children: [
// TextField(
// autofocus: true,
// textAlign: TextAlign.center,
// onSubmitted: (text) {
// print('onSubmitted:${text}');
// suggestion.dispatch(SuggestionFetch(query: text));
// },
// ),
Expanded(
child: BlocBuilder(
bloc: suggestion,
builder: (BuildContext context, SuggestionState state) {
print('BlocBuilder----${state}');
if (state is SuggestionUninitialized) {
return Center(
child: Text('暂无内容'),
);
} else if (state is SuggestionLoading) {
return Center(
child: CircularProgressIndicator(),
);
} else if (state is SuggestionError) {
return Center(
child: Text('出现错误'),
);
} else if (state is SuggestionLoaded) {
if (state.res.length == 0) {
return Center(
child: Text('没有适合的结果,更换查询条件试试'),
);
}else {
if (widget.itemTitle is Function) {
return widget.itemTitle(state);
}
}
}
},
),
),
],
),
);
}
@override
void dispose() {
//suggestion.dispose();//添加这个第二次进入会失灵
super.dispose();
}
}

View File

@ -0,0 +1,72 @@
/**
* Created with Android Studio.
* User: 一晟
* Date: 2019/4/28
* Time: 3:19 PM
* email: zhu.yan@alibaba-inc.com
*/
class Suggestion {
String query;
List<Suggestions> suggestions;
int code;
Suggestion({this.query, this.suggestions, this.code});
Suggestion.fromJson(Map<String, dynamic> json) {
query = json['query'];
if (json['suggestions'] != null) {
suggestions = new List<Suggestions>();
json['suggestions'].forEach((v) {
suggestions.add(new Suggestions.fromJson(v));
});
}
code = json['code'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['query'] = this.query;
if (this.suggestions != null) {
data['suggestions'] = this.suggestions.map((v) => v.toJson()).toList();
}
data['code'] = this.code;
return data;
}
}
class Suggestions {
Data data;
String value;
Suggestions({this.data, this.value});
Suggestions.fromJson(Map<String, dynamic> json) {
data = json['data'] != null ? new Data.fromJson(json['data']) : null;
value = json['value'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.data != null) {
data['data'] = this.data.toJson();
}
data['value'] = this.value;
return data;
}
}
class Data {
String category;
Data({this.category});
Data.fromJson(Map<String, dynamic> json) {
category = json['category'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['category'] = this.category;
return data;
}
}

View File

@ -0,0 +1,34 @@
/**
* Created with Android Studio.
* User: 一晟
* Date: 2019/4/28
* Time: 3:37 PM
* email: zhu.yan@alibaba-inc.com
*/
abstract class SuggestionState {}
class SuggestionError extends SuggestionState {
@override
String toString() => 'SuggestionError获取失败';
}
class SuggestionUninitialized extends SuggestionState {
@override
String toString() => 'SuggestionUninitialized未初始化';
}
class SuggestionLoading extends SuggestionState {
@override
String toString() => 'SuggestionLoading :正在加载';
}
class SuggestionLoaded extends SuggestionState {
final List res;
SuggestionLoaded({
this.res,
});
@override
String toString() => 'SuggestionLoaded加载完毕';
}

View File

@ -0,0 +1,14 @@
/**
* Created with Android Studio.
* User: 一晟
* Date: 2019/4/28
* Time: 7:11 PM
* email: zhu.yan@alibaba-inc.com
* tartget:
*/
class SearchResult {
String title;
String source;
SearchResult({this.title, this.source});
}

View File

@ -42,6 +42,18 @@ class _BannerState extends State<HomeBanner> {
timer.cancel();
}
/// pagination 的计数器
Widget _numberIndicator(BuildContext context, int index, int itemCount) {
return Container(
decoration: BoxDecoration(
color: Colors.black45, borderRadius: BorderRadius.circular(20.0)),
margin: EdgeInsets.only(top: 10.0, right: 5.0),
padding: EdgeInsets.symmetric(horizontal: 6.0, vertical: 2.0),
child: Text("${++index}/$itemCount",
style: TextStyle( color: Colors.white70, fontSize: 12.0, fontWeight:FontWeight.bold )),
);
}
@override
Widget build(BuildContext context) {
return Container(
@ -54,6 +66,11 @@ class _BannerState extends State<HomeBanner> {
onPageChanged: _onPageChanged,
children: _buildItems(),),
_buildIndicator(), // 下面的小点
Positioned(//方法二
top: 0.0,
right: 0.0,
child: _numberIndicator(context,virtualIndex,widget.bannerStories.length),
)
]),
);
}

View File

@ -6,7 +6,7 @@ import 'routers/application.dart';
import 'package:flutter_go/utils/provider.dart';
import 'package:flutter_go/utils/shared_preferences.dart';
import 'package:flutter_go/views/first_page/home.dart';
import 'package:flutter_go/views/home.dart';
import 'package:flutter_go/model/search_history.dart';
import 'package:flutter_go/utils/analytics.dart' as Analytics;
//import 'views/welcome_page/index.dart';

View File

@ -5,7 +5,7 @@ import 'package:flutter_go/components/category.dart';
import '../widgets/404.dart';
import 'package:flutter_go/components/full_screen_code_dialog.dart';
import 'package:flutter_go/views/web_page/web_view_page.dart';
import 'package:flutter_go/views/first_page/home.dart';
import 'package:flutter_go/views/home.dart';
// app的首页
var homeHandler = new Handler(

View File

@ -80,8 +80,8 @@ class FirstPageState extends State<FirstPage> with AutomaticKeepAliveClientMixin
return result;
}
/// 每个item的样式
Widget makeCard(index,item){
var myTitle = '${item.title}';
var myUsername = '${'👲'}: ${item.username} ';
var codeUrl = '${item.detailUrl}';
@ -92,18 +92,18 @@ class FirstPageState extends State<FirstPage> with AutomaticKeepAliveClientMixin
return
Column(
children: <Widget>[
Stack(
//alignment: const FractionalOffset(0.9, 0.1),//方法一
children: <Widget>[
Pagination(),
Positioned(//方法二
top: 10.0,
left: 0.0,
child: DisclaimerMsg(key:key,pWidget:this)
),
]),
SizedBox(height: 1, child:Container(color: Theme.of(context).primaryColor)),
SizedBox(height: 10),
Stack(
//alignment: const FractionalOffset(0.9, 0.1),//方法一
children: <Widget>[
Pagination(),
Positioned(//方法二
top: 10.0,
left: 0.0,
child: DisclaimerMsg(key:key,pWidget:this)
),
]),
SizedBox(height: 1, child:Container(color: Theme.of(context).primaryColor)),
SizedBox(height: 10),
],
);

View File

@ -0,0 +1,415 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
const double _kLeadingWidth =
kToolbarHeight; // So the leading button is square.
// Bottom justify the kToolbarHeight child which may overflow the top.
class _ToolbarContainerLayout extends SingleChildLayoutDelegate {
const _ToolbarContainerLayout();
@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
return constraints.tighten(height: kToolbarHeight);
}
@override
Size getSize(BoxConstraints constraints) {
return Size(constraints.maxWidth, kToolbarHeight);
}
@override
Offset getPositionForChild(Size size, Size childSize) {
return Offset(0.0, size.height - childSize.height);
}
@override
bool shouldRelayout(_ToolbarContainerLayout oldDelegate) => false;
}
class MyAppBar extends StatefulWidget implements PreferredSizeWidget {
/// Creates a material design app bar.
///
/// The arguments [elevation], [primary], [toolbarOpacity], [bottomOpacity]
/// and [automaticallyImplyLeading] must not be null.
///
/// Typically used in the [Scaffold.appBar] property.
MyAppBar({
Key key,
this.leading,
this.automaticallyImplyLeading = true,
this.title,
this.actions,
this.flexibleSpace,
this.bottom,
this.elevation = 4.0,
this.backgroundColor,
this.brightness,
this.iconTheme,
this.textTheme,
this.primary = true,
this.centerTitle,
this.titleSpacing = NavigationToolbar.kMiddleSpacing,
this.toolbarOpacity = 1.0,
this.bottomOpacity = 1.0,
}) : assert(automaticallyImplyLeading != null),
assert(elevation != null),
assert(primary != null),
assert(titleSpacing != null),
assert(toolbarOpacity != null),
assert(bottomOpacity != null),
preferredSize = Size.fromHeight(
kToolbarHeight + (bottom?.preferredSize?.height ?? 0.0)),
super(key: key);
/// A widget to display before the [title].
///
/// If this is null and [automaticallyImplyLeading] is set to true, the
/// [AppBar] will imply an appropriate widget. For example, if the [AppBar] is
/// in a [Scaffold] that also has a [Drawer], the [Scaffold] will fill this
/// widget with an [IconButton] that opens the drawer (using [Icons.menu]). If
/// there's no [Drawer] and the parent [Navigator] can go back, the [AppBar]
/// will use a [BackButton] that calls [Navigator.maybePop].
final Widget leading;
/// Controls whether we should try to imply the leading widget if null.
///
/// If true and [leading] is null, automatically try to deduce what the leading
/// widget should be. If false and [leading] is null, leading space is given to [title].
/// If leading widget is not null, this parameter has no effect.
final bool automaticallyImplyLeading;
/// The primary widget displayed in the appbar.
///
/// Typically a [Text] widget containing a description of the current contents
/// of the app.
final Widget title;
/// Widgets to display after the [title] widget.
///
/// Typically these widgets are [IconButton]s representing common operations.
/// For less common operations, consider using a [PopupMenuButton] as the
/// last action.
///
/// ## Sample code
///
/// ```dart
/// Scaffold(
/// appBar: AppBar(
/// title: Text('Hello World'),
/// actions: <Widget>[
/// IconButton(
/// icon: Icon(Icons.shopping_cart),
/// tooltip: 'Open shopping cart',
/// onPressed: () {
/// // ...
/// },
/// ),
/// ],
/// ),
/// )
/// ```
final List<Widget> actions;
/// This widget is stacked behind the toolbar and the tabbar. It's height will
/// be the same as the app bar's overall height.
///
/// A flexible space isn't actually flexible unless the [AppBar]'s container
/// changes the [AppBar]'s size. A [SliverAppBar] in a [CustomScrollView]
/// changes the [AppBar]'s height when scrolled.
///
/// Typically a [FlexibleSpaceBar]. See [FlexibleSpaceBar] for details.
final Widget flexibleSpace;
/// This widget appears across the bottom of the app bar.
///
/// Typically a [TabBar]. Only widgets that implement [PreferredSizeWidget] can
/// be used at the bottom of an app bar.
///
/// See also:
///
/// * [PreferredSize], which can be used to give an arbitrary widget a preferred size.
final PreferredSizeWidget bottom;
/// The z-coordinate at which to place this app bar. This controls the size of
/// the shadow below the app bar.
///
/// Defaults to 4, the appropriate elevation for app bars.
final double elevation;
/// The color to use for the app bar's material. Typically this should be set
/// along with [brightness], [iconTheme], [textTheme].
///
/// Defaults to [ThemeData.primaryColor].
final Color backgroundColor;
/// The brightness of the app bar's material. Typically this is set along
/// with [backgroundColor], [iconTheme], [textTheme].
///
/// Defaults to [ThemeData.primaryColorBrightness].
final Brightness brightness;
/// The color, opacity, and size to use for app bar icons. Typically this
/// is set along with [backgroundColor], [brightness], [textTheme].
///
/// Defaults to [ThemeData.primaryIconTheme].
final IconThemeData iconTheme;
/// The typographic styles to use for text in the app bar. Typically this is
/// set along with [brightness] [backgroundColor], [iconTheme].
///
/// Defaults to [ThemeData.primaryTextTheme].
final TextTheme textTheme;
/// Whether this app bar is being displayed at the top of the screen.
///
/// If true, the appbar's toolbar elements and [bottom] widget will be
/// padded on top by the height of the system status bar. The layout
/// of the [flexibleSpace] is not affected by the [primary] property.
final bool primary;
/// Whether the title should be centered.
///
/// Defaults to being adapted to the current [TargetPlatform].
final bool centerTitle;
/// The spacing around [title] content on the horizontal axis. This spacing is
/// applied even if there is no [leading] content or [actions]. If you want
/// [title] to take all the space available, set this value to 0.0.
///
/// Defaults to [NavigationToolbar.kMiddleSpacing].
final double titleSpacing;
/// How opaque the toolbar part of the app bar is.
///
/// A value of 1.0 is fully opaque, and a value of 0.0 is fully transparent.
///
/// Typically, this value is not changed from its default value (1.0). It is
/// used by [SliverAppBar] to animate the opacity of the toolbar when the app
/// bar is scrolled.
final double toolbarOpacity;
/// How opaque the bottom part of the app bar is.
///
/// A value of 1.0 is fully opaque, and a value of 0.0 is fully transparent.
///
/// Typically, this value is not changed from its default value (1.0). It is
/// used by [SliverAppBar] to animate the opacity of the toolbar when the app
/// bar is scrolled.
final double bottomOpacity;
/// A size whose height is the sum of [kToolbarHeight] and the [bottom] widget's
/// preferred height.
///
/// [Scaffold] uses this this size to set its app bar's height.
@override
final Size preferredSize;
bool _getEffectiveCenterTitle(ThemeData themeData) {
if (centerTitle != null) return centerTitle;
assert(themeData.platform != null);
switch (themeData.platform) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
return false;
case TargetPlatform.iOS:
return actions == null || actions.length < 2;
}
return null;
}
@override
_MyAppBarState createState() => _MyAppBarState();
}
class _MyAppBarState extends State<MyAppBar> {
void _handleDrawerButton() {
Scaffold.of(context).openDrawer();
}
void _handleDrawerButtonEnd() {
Scaffold.of(context).openEndDrawer();
}
@override
Widget build(BuildContext context) {
assert(!widget.primary || debugCheckHasMediaQuery(context));
assert(debugCheckHasMaterialLocalizations(context));
final ThemeData themeData = Theme.of(context);
final ScaffoldState scaffold = Scaffold.of(context, nullOk: true);
final ModalRoute<dynamic> parentRoute = ModalRoute.of(context);
final bool hasDrawer = scaffold?.hasDrawer ?? false;
final bool hasEndDrawer = scaffold?.hasEndDrawer ?? false;
final bool canPop = parentRoute?.canPop ?? false;
final bool useCloseButton =
parentRoute is PageRoute<dynamic> && parentRoute.fullscreenDialog;
IconThemeData appBarIconTheme =
widget.iconTheme ?? themeData.primaryIconTheme;
TextStyle centerStyle =
widget.textTheme?.title ?? themeData.primaryTextTheme.title;
TextStyle sideStyle =
widget.textTheme?.body1 ?? themeData.primaryTextTheme.body1;
if (widget.toolbarOpacity != 1.0) {
final double opacity =
const Interval(0.25, 1.0, curve: Curves.fastOutSlowIn)
.transform(widget.toolbarOpacity);
if (centerStyle?.color != null)
centerStyle =
centerStyle.copyWith(color: centerStyle.color.withOpacity(opacity));
if (sideStyle?.color != null)
sideStyle =
sideStyle.copyWith(color: sideStyle.color.withOpacity(opacity));
appBarIconTheme = appBarIconTheme.copyWith(
opacity: opacity * (appBarIconTheme.opacity ?? 1.0));
}
Widget leading = widget.leading;
//if (leading == null && widget.automaticallyImplyLeading) {
if (widget.automaticallyImplyLeading) {
if (hasDrawer) {
leading = IconButton(
//icon: const Icon(Icons.menu),
icon: leading ?? const Icon(Icons.menu),
onPressed: _handleDrawerButton,
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
);
} else {
if (canPop)
leading = useCloseButton ? const CloseButton() : const BackButton();
}
}
if (leading != null) {
leading = ConstrainedBox(
constraints: const BoxConstraints.tightFor(width: _kLeadingWidth),
child: leading,
);
}
Widget title = widget.title;
if (title != null) {
bool namesRoute;
switch (defaultTargetPlatform) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
namesRoute = true;
break;
case TargetPlatform.iOS:
break;
}
title = DefaultTextStyle(
style: centerStyle,
softWrap: false,
overflow: TextOverflow.ellipsis,
child: Semantics(
namesRoute: namesRoute,
child: title,
header: true,
),
);
}
Widget actions;
if (widget.actions != null && widget.actions.isNotEmpty) {
actions = Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: widget.actions,
);
} else if (hasEndDrawer) {
actions = IconButton(
icon: const Icon(Icons.menu),
onPressed: _handleDrawerButtonEnd,
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
);
}
final Widget toolbar = NavigationToolbar(
leading: leading,
middle: title,
trailing: actions,
centerMiddle: widget._getEffectiveCenterTitle(themeData),
middleSpacing: widget.titleSpacing,
);
// If the toolbar is allocated less than kToolbarHeight make it
// appear to scroll upwards within its shrinking container.
Widget appBar = ClipRect(
child: CustomSingleChildLayout(
delegate: const _ToolbarContainerLayout(),
child: IconTheme.merge(
data: appBarIconTheme,
child: DefaultTextStyle(
style: sideStyle,
child: toolbar,
),
),
),
);
if (widget.bottom != null) {
appBar = Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Flexible(
child: ConstrainedBox(
constraints: const BoxConstraints(maxHeight: kToolbarHeight),
child: appBar,
),
),
widget.bottomOpacity == 1.0
? widget.bottom
: Opacity(
opacity:
const Interval(0.25, 1.0, curve: Curves.fastOutSlowIn)
.transform(widget.bottomOpacity),
child: widget.bottom,
),
],
);
}
// The padding applies to the toolbar and tabbar, not the flexible space.
if (widget.primary) {
appBar = SafeArea(
top: true,
child: appBar,
);
}
appBar = Align(
alignment: Alignment.topCenter,
child: appBar,
);
if (widget.flexibleSpace != null) {
appBar = Stack(
fit: StackFit.passthrough,
children: <Widget>[
widget.flexibleSpace,
appBar,
],
);
}
final Brightness brightness =
widget.brightness ?? themeData.primaryColorBrightness;
final SystemUiOverlayStyle overlayStyle = brightness == Brightness.dark
? SystemUiOverlayStyle.light
: SystemUiOverlayStyle.dark;
return Semantics(
container: true,
explicitChildNodes: true,
child: AnnotatedRegion<SystemUiOverlayStyle>(
value: overlayStyle,
child: Material(
color: widget.backgroundColor ?? themeData.primaryColor,
elevation: widget.elevation,
child: appBar,
),
),
);
}
}

View File

@ -0,0 +1,106 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import './first_page.dart';
import './sub_page.dart';
import './main_app_bar.dart';
import './search_page.dart';
class _Page {
final String labelId;
_Page(this.labelId);
}
final List<_Page> _allPages = <_Page>[
_Page('项目1'),
_Page('项目2'),
_Page('项目3'),
_Page('项目4'),
];
class MainPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("MainPagess build......");
return DefaultTabController(
length: _allPages.length,
child: Scaffold(
appBar: new MyAppBar(
leading: Container(
child: new ClipOval(
child: Image.network(
'https://hbimg.huabanimg.com/9bfa0fad3b1284d652d370fa0a8155e1222c62c0bf9d-YjG0Vt_fw658',
scale: 15.0,
),
)
),
centerTitle: true,
title: TabLayout(),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
onPressed: () {
pushPage(context, SearchPage(), pageName: "SearchPage");
})
],
),
body: TabBarViewLayout(),
// drawer: Drawer(
// child: MainLeftPage(),
// ),
));
}
}
void pushPage(BuildContext context, Widget page, {String pageName}) {
if (context == null || page == null) return;
Navigator.push(context, CupertinoPageRoute<void>(builder: (ctx) => page));
}
class TabLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return TabBar(
isScrollable: true,
//labelPadding: EdgeInsets.all(12.0),
labelPadding: EdgeInsets.only(top: 12.0,left: 12.0,right:12.0),
indicatorSize: TabBarIndicatorSize.label,
tabs: _allPages
.map((_Page page) =>
Tab(text: page.labelId))
.toList(),
);
}
}
class TabBarViewLayout extends StatelessWidget {
Widget buildTabView(BuildContext context, _Page page) {
String labelId = page.labelId;
switch (labelId) {
case '项目1':
return FirstPage();
break;
case '项目2':
return SubPage();
break;
case '项目3':
return SubPage();
break;
case '项目4':
return SubPage();
break;
default:
return Container();
break;
}
}
@override
Widget build(BuildContext context) {
print("TabBarViewLayout build.......");
return TabBarView(
children: _allPages.map((_Page page) {
return buildTabView(context, page);
}).toList());
}
}

View File

@ -0,0 +1,189 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/cupertino.dart';
import 'dart:ui';
import 'dart:async';
import 'package:flutter_go/blocs/industry_main.dart' as Industry;
import 'package:flutter_go/blocs/industry_event.dart';
import 'package:flutter_go/routers/application.dart';
import 'package:flutter_go/routers/routers.dart';
final _industryPage = Industry.IndustryPage(itemTitle: (state){
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
return ListTile(
dense: true,
leading: Icon(
Icons.bookmark_border,
size: 32,
),
title: Text(
state.res[index].title,
overflow: TextOverflow.ellipsis,
),
subtitle: Text(state.res[index].source),
onTap: () {
// 在这里对选中的结果进行解析因为我目前是用golang实现的所以就没贴代码了。
print(state.res[index].source);
final itemTitle = state.res[index].title;
final itemUrl = state.res[index].source;
Application.router.navigateTo(context, '${Routes.webViewPage}?title=${Uri.encodeComponent(itemTitle)}&url=${Uri.encodeComponent(itemUrl)}');
},
);
},
itemCount: state.res.length,
);
});
final suggestion = Industry.suggestion;
final searchBarPage = SearchBarPage();
class SearchPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
/// print('suggestion::${Industry.suggestion}');
return Scaffold(
appBar: PreferredSize(
preferredSize: Size(double.infinity, 52), // 44 is the height
child: AppBar(title: searchBarPage)
),
//body: ProgressView(),
body: _industryPage,
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.search),
onPressed: () {
//print('searchBarPage=====>${controller.text}');
//print('searchBarPage=====>${that.getResultsDebounced}');
if(that is _SearchBarPageState && that.getResultsDebounced is Function && controller.text is String ) {
that.getResultsDebounced(controller.text);
}
},
),
);
}
}
/// 加载loading
class ProgressView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: SizedBox(
width: 24.0,
height: 24.0,
child: CircularProgressIndicator(
strokeWidth: 2.0,
),
),
);
}
}
class SearchBarPage extends StatefulWidget{
@override
State<StatefulWidget> createState() => _SearchBarPageState();
}
final TextEditingController controller = TextEditingController();
var that;
class _SearchBarPageState extends State<SearchBarPage> {
@override
void initState() {
super.initState();
that = this;
}
Timer _resultsTimer;
bool _loading = false;
String oldKey;
/// 防抖函数
Future getResultsDebounced(String text) async {
if(oldKey == text){
print('请求内容重复');
return;
}
if (text == '' || !mounted) {
return;
}
_loading = true;
if (_resultsTimer != null && _resultsTimer.isActive) {
_resultsTimer.cancel();
}
_resultsTimer = new Timer(new Duration(milliseconds: 400), () async {
_loading = true;
if(mounted){
suggestion.dispatch(SuggestionFetch(query: text));
}
oldKey = text;
});
}
void onSearchTextChanged(String text){
print('onSearchTextChanged:${text}');
//suggestion.dispatch(SuggestionFetch(query: text));
getResultsDebounced(text);
}
@override
void dispose() {
print('dispose');
suggestion.dispatch(SuggestionClearFetch());
//controller.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Container(
color: Theme.of(context).primaryColor,
//color: Colors.amber,
child: Padding(
//padding: EdgeInsets.only(top: MediaQueryData.fromWindow(window).padding.top,),
padding: EdgeInsets.all(0.0),
child: Container(
//height: 162.0,
child: Padding(
padding: const EdgeInsets.all(6.0),
child: Card(
child: Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(width: 5.0,),
Icon(Icons.search, color: Colors.grey,size: 18.0,),
Expanded(
child: Container(
alignment: Alignment.center,
child: TextField(
controller: controller,
decoration: InputDecoration(
contentPadding: EdgeInsets.only(top: 0.0),
hintText: '搜索全局flutter知识库',
hintStyle:TextStyle(fontSize: 12.0),
border: InputBorder.none
),
onChanged: onSearchTextChanged,
),
),
),
IconButton(
icon: Icon(Icons.cancel),
color: Colors.grey,
iconSize: 18.0,
onPressed: () {
controller.clear();
// onSearchTextChanged('');
},
),
],
),
)
)
),
),
),
);
}
}

View File

@ -0,0 +1,125 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter_go/components/list_view_item.dart';
import 'package:flutter_go/components/list_refresh.dart' as listComp;
import 'package:flutter_go/components/pagination.dart';
import 'package:flutter_go/views/first_page/first_page_item.dart';
import 'package:flutter_go/components/disclaimer_msg.dart';
import 'package:flutter_go/utils/net_utils.dart';
// ValueKey<String> key;
class SubPage extends StatefulWidget {
@override
SubPageState createState() => SubPageState();
}
class SubPageState extends State<SubPage> with AutomaticKeepAliveClientMixin{
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
Future<bool> _unKnow;
GlobalKey<DisclaimerMsgState> key;
@override
bool get wantKeepAlive => true;
@override
void initState() {
super.initState();
if (key == null) {
key = GlobalKey<DisclaimerMsgState>();
// key = const Key('__RIKEY1__');
//获取sharePre
_unKnow = _prefs.then((SharedPreferences prefs) {
return (prefs.getBool('disclaimer::Boolean') ?? false);
});
/// 判断是否需要弹出免责声明,已经勾选过不在显示,就不会主动弹
_unKnow.then((bool value) {
new Future.delayed(const Duration(seconds: 1),(){
if (!value && key.currentState is DisclaimerMsgState && key.currentState.showAlertDialog is Function) {
key.currentState.showAlertDialog(context);
}
});
});
}
}
Future<Map> getIndexListData([Map<String, dynamic> params]) async {
const juejin_flutter = 'https://timeline-merger-ms.juejin.im/v1/get_tag_entry?src=web&tagId=5a96291f6fb9a0535b535438';
var pageIndex = (params is Map) ? params['pageIndex'] : 0;
final _param = {'page':pageIndex,'pageSize':20,'sort':'rankIndex'};
var responseList = [];
var pageTotal = 0;
try{
var response = await NetUtils.get(juejin_flutter, params: _param);
responseList = response['d']['entrylist'];
pageTotal = response['d']['total'];
if (!(pageTotal is int) || pageTotal <= 0) {
pageTotal = 0;
}
}catch(e){
}
pageIndex += 1;
List resultList = new List();
for (int i = 0; i < responseList.length; i++) {
try {
FirstPageItem cellData = new FirstPageItem.fromJson(responseList[i]);
resultList.add(cellData);
} catch (e) {
// No specified type, handles all
}
}
Map<String, dynamic> result = {"list":resultList, 'total':pageTotal, 'pageIndex':pageIndex};
return result;
}
Widget makeCard(index,item){
var myTitle = '${item.title}';
var myUsername = '${'👲'}: ${item.username} ';
var codeUrl = '${item.detailUrl}';
return new ListViewItem(itemUrl:codeUrl,itemTitle: myTitle,data: myUsername,);
}
headerView(){
return
Column(
children: <Widget>[
Stack(
//alignment: const FractionalOffset(0.9, 0.1),//方法一
children: <Widget>[
Pagination(),
Positioned(//方法二
top: 10.0,
left: 0.0,
child: DisclaimerMsg(key:key,pWidget:this)
),
]),
SizedBox(height: 1, child:Container(color: Theme.of(context).primaryColor)),
SizedBox(height: 10),
],
);
}
@override
Widget build(BuildContext context) {
super.build(context);
return new Column(
children: <Widget>[
new Expanded(
child: listComp.ListRefresh(getIndexListData,makeCard)
)
]
);
}
}

View File

@ -22,6 +22,10 @@ import 'package:flutter_go/components/search_input.dart';
import 'package:flutter_go/model/search_history.dart';
import 'package:flutter_go/resources/widget_name_to_icon.dart';
import './first_page/main_page.dart';
const int ThemeColor = 0xFFC91B3A;
class AppPage extends StatefulWidget {
@ -92,12 +96,14 @@ class _MyHomePageState extends State<AppPage>
});
searchHistoryList
.add(SearchHistory(name: targetName, targetRouter: targetRouter));
print("searchHistoryList ${searchHistoryList.toString()}");
print("searchHistoryList1 ${searchHistoryList.toString()}");
print("searchHistoryList2 ${targetRouter}");
print("searchHistoryList3 ${widgetPoint.name}");
Application.router.navigateTo(context, "$targetRouter");
}
Widget buildSearchInput(BuildContext context) {
return new SearchInput((value) async {
return new SearchInput((value) async {
if (value != '') {
List<WidgetPoint> list = await widgetControl.search(value);
@ -107,7 +113,7 @@ class _MyHomePageState extends State<AppPage>
icon: WidgetName2Icon.icons[item.name] ?? null,
text: 'widget',
onTap: () {
onWidgetTap(item, context);
onWidgetTap(item, context);
},
))
.toList();
@ -117,16 +123,25 @@ class _MyHomePageState extends State<AppPage>
}, (value) {}, () {});
}
renderAppBar(BuildContext context,Widget widget) {
print('renderAppBar=====>>>>>>${controller.index}');
if(controller.index == 0) {
return null;
}
return AppBar(title: buildSearchInput(context));
}
@override
Widget build(BuildContext context) {
var db = Provider.db;
return new Scaffold(
appBar: new AppBar(title: buildSearchInput(context)),
appBar: renderAppBar(context,widget),
body: new TabBarView(controller: controller, children: <Widget>[
new FirstPage(),
new WidgetPage(db),
new CollectionPage(),
//new FirstPage(),
MainPage(),
WidgetPage(db),
CollectionPage(),
FourthPage()
]),
bottomNavigationBar: Material(

View File

@ -35,6 +35,9 @@ dependencies:
firebase_analytics: ^1.1.0
#firebase_auth: ^0.8.3 #auth
firebase_core: ^0.3.4 # add dependency for Firebase Core
flutter_bloc: ^0.11.1
bloc: ^0.12.0
html: ^0.14.0+2
dev_dependencies:
flutter_test: