mirror of
https://github.com/alibaba/flutter-go.git
synced 2025-07-04 07:18:11 +08:00
BIN
assets/images/ali_connors.png
Normal file
BIN
assets/images/ali_connors.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
BIN
flutter_01.png
Normal file
BIN
flutter_01.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 41 KiB |
35
lib/blocs/bak/search_api.dart
Normal file
35
lib/blocs/bak/search_api.dart
Normal 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();
|
35
lib/blocs/bak/search_bloc.dart
Normal file
35
lib/blocs/bak/search_bloc.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
18
lib/blocs/bak/search_event.dart
Normal file
18
lib/blocs/bak/search_event.dart
Normal 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:获取搜索结果事件';
|
||||
}
|
14
lib/blocs/bak/search_result.dart
Normal file
14
lib/blocs/bak/search_result.dart
Normal 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});
|
||||
}
|
37
lib/blocs/bak/search_state.dart
Normal file
37
lib/blocs/bak/search_state.dart
Normal 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:加载完毕';
|
||||
}
|
80
lib/blocs/bak/search_widget.dart
Normal file
80
lib/blocs/bak/search_widget.dart
Normal 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,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
71
lib/blocs/industry_api.dart
Normal file
71
lib/blocs/industry_api.dart
Normal 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();
|
41
lib/blocs/industry_bloc.dart
Normal file
41
lib/blocs/industry_bloc.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
26
lib/blocs/industry_event.dart
Normal file
26
lib/blocs/industry_event.dart
Normal 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:清空界面';
|
||||
}
|
79
lib/blocs/industry_main.dart
Normal file
79
lib/blocs/industry_main.dart
Normal 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();
|
||||
}
|
||||
}
|
72
lib/blocs/industry_model.dart
Normal file
72
lib/blocs/industry_model.dart
Normal 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;
|
||||
}
|
||||
}
|
34
lib/blocs/industry_state.dart
Normal file
34
lib/blocs/industry_state.dart
Normal 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:加载完毕';
|
||||
}
|
14
lib/blocs/search_result.dart
Normal file
14
lib/blocs/search_result.dart
Normal 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});
|
||||
}
|
@ -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),
|
||||
)
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -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(
|
||||
|
@ -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),
|
||||
],
|
||||
);
|
||||
|
||||
|
415
lib/views/first_page/main_app_bar.dart
Normal file
415
lib/views/first_page/main_app_bar.dart
Normal 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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
106
lib/views/first_page/main_page.dart
Normal file
106
lib/views/first_page/main_page.dart
Normal 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());
|
||||
}
|
||||
}
|
189
lib/views/first_page/search_page.dart
Normal file
189
lib/views/first_page/search_page.dart
Normal 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('');
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
125
lib/views/first_page/sub_page.dart
Normal file
125
lib/views/first_page/sub_page.dart
Normal 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)
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(
|
@ -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:
|
||||
|
Reference in New Issue
Block a user