基础库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

250 lines
7.1 KiB

  1. part of let_log;
  2. class LogWidget extends StatefulWidget {
  3. const LogWidget({Key key}) : super(key: key);
  4. @override
  5. _LogWidgetState createState() => _LogWidgetState();
  6. }
  7. class _LogWidgetState extends State<LogWidget> {
  8. bool _showSearch = false;
  9. String _keyword = "";
  10. TextEditingController _textController;
  11. ScrollController _scrollController;
  12. FocusNode _focusNode;
  13. bool _goDown = true;
  14. @override
  15. void initState() {
  16. _textController = TextEditingController(text: _keyword);
  17. _scrollController = ScrollController();
  18. _focusNode = FocusNode();
  19. super.initState();
  20. }
  21. @override
  22. void dispose() {
  23. _textController.dispose();
  24. _scrollController.dispose();
  25. super.dispose();
  26. }
  27. @override
  28. Widget build(BuildContext context) {
  29. return Scaffold(
  30. body: Column(
  31. crossAxisAlignment: CrossAxisAlignment.start,
  32. children: <Widget>[
  33. _buildTools(),
  34. Expanded(
  35. child: ValueListenableBuilder<int>(
  36. valueListenable: _Log.length,
  37. builder: (context, value, child) {
  38. List<_Log> logs = _Log.list;
  39. if (_selectTypes.length < 4 || _keyword.isNotEmpty) {
  40. logs = _Log.list.where((test) {
  41. return _selectTypes.contains(test.type) &&
  42. test.contains(_keyword);
  43. }).toList();
  44. }
  45. final len = logs.length;
  46. return ListView.separated(
  47. itemBuilder: (context, index) {
  48. final item = Logger.config.reverse
  49. ? logs[len - index - 1]
  50. : logs[index];
  51. final color = _getColor(item.type, context);
  52. return _buildItem(item, color);
  53. },
  54. itemCount: len,
  55. controller: _scrollController,
  56. separatorBuilder: (context, index) {
  57. return const Divider(
  58. height: 10,
  59. thickness: 0.5,
  60. color: Color(0xFFE0E0E0),
  61. );
  62. },
  63. );
  64. },
  65. ),
  66. ),
  67. ],
  68. ),
  69. floatingActionButton: FloatingActionButton(
  70. onPressed: () {
  71. if (_goDown) {
  72. _scrollController.animateTo(
  73. _scrollController.position.maxScrollExtent * 2,
  74. curve: Curves.easeOut,
  75. duration: const Duration(milliseconds: 300),
  76. );
  77. } else {
  78. _scrollController.animateTo(
  79. 0,
  80. curve: Curves.easeOut,
  81. duration: const Duration(milliseconds: 300),
  82. );
  83. }
  84. _goDown = !_goDown;
  85. setState(() {});
  86. },
  87. mini: true,
  88. child: Icon(
  89. _goDown ? Icons.arrow_downward : Icons.arrow_upward,
  90. ),
  91. ),
  92. );
  93. }
  94. Widget _buildItem(_Log item, Color color) {
  95. return InkWell(
  96. onTap: () {
  97. final ClipboardData data = ClipboardData(text: item.toString());
  98. Clipboard.setData(data);
  99. showDialog(
  100. context: context,
  101. barrierDismissible: true,
  102. builder: (context) {
  103. return const Center(
  104. child: Material(
  105. color: Colors.transparent,
  106. child: Text(
  107. "copy success!",
  108. style: TextStyle(color: Colors.white, fontSize: 30),
  109. ),
  110. ),
  111. );
  112. },
  113. );
  114. },
  115. child: Padding(
  116. padding: const EdgeInsets.fromLTRB(16, 8, 16, 8),
  117. child: Column(
  118. crossAxisAlignment: CrossAxisAlignment.start,
  119. children: [
  120. Text(
  121. "${item.tabName} ${item.message} (${item.start.hour}:${item.start.minute}:${item.start.second}:${item.start.millisecond})",
  122. style: TextStyle(fontSize: 16, color: color),
  123. ),
  124. // if (item.detail != null)
  125. Visibility(
  126. visible: item.detail != null,
  127. child: Padding(
  128. padding: const EdgeInsets.only(top: 8),
  129. child: Text(
  130. item.detail,
  131. style: TextStyle(fontSize: 14, color: color),
  132. overflow: TextOverflow.ellipsis,
  133. maxLines: 20,
  134. ),
  135. ),
  136. )
  137. ],
  138. ),
  139. ),
  140. );
  141. }
  142. Color _getColor(_Type type, BuildContext context) {
  143. switch (type) {
  144. case _Type.debug:
  145. return Colors.blue;
  146. case _Type.warn:
  147. return const Color(0xFFF57F17);
  148. case _Type.error:
  149. return Colors.red;
  150. default:
  151. return Theme.of(context).textTheme.body1.color;
  152. }
  153. }
  154. final List<_Type> _selectTypes = [
  155. _Type.log,
  156. _Type.debug,
  157. _Type.warn,
  158. _Type.error
  159. ];
  160. Widget _buildTools() {
  161. final List<ChoiceChip> arr = [];
  162. _Type.values.forEach((f) {
  163. arr.add(
  164. ChoiceChip(
  165. label: Text(
  166. _getTabName(f.index),
  167. style: const TextStyle(fontSize: 14),
  168. ),
  169. selectedColor: const Color(0xFFCBE2F6),
  170. selected: _selectTypes.contains(f),
  171. onSelected: (value) {
  172. _selectTypes.contains(f)
  173. ? _selectTypes.remove(f)
  174. : _selectTypes.add(f);
  175. setState(() {});
  176. },
  177. ),
  178. );
  179. });
  180. return Padding(
  181. padding: const EdgeInsets.fromLTRB(16, 5, 0, 5),
  182. child: AnimatedCrossFade(
  183. crossFadeState:
  184. _showSearch ? CrossFadeState.showSecond : CrossFadeState.showFirst,
  185. duration: const Duration(milliseconds: 300),
  186. firstChild: Row(
  187. children: [
  188. Expanded(
  189. child: Wrap(
  190. spacing: 5,
  191. children: arr,
  192. ),
  193. ),
  194. const IconButton(
  195. icon: Icon(Icons.clear),
  196. onPressed: _Log.clear,
  197. ),
  198. IconButton(
  199. icon: _keyword.isEmpty
  200. ? const Icon(Icons.search)
  201. : const Icon(Icons.filter_1),
  202. onPressed: () {
  203. _showSearch = true;
  204. setState(() {});
  205. _focusNode.requestFocus();
  206. },
  207. ),
  208. ],
  209. ),
  210. secondChild: Row(
  211. children: [
  212. Expanded(
  213. child: SizedBox(
  214. height: 36,
  215. child: TextField(
  216. decoration: const InputDecoration(
  217. border: OutlineInputBorder(),
  218. contentPadding: EdgeInsets.all(6),
  219. ),
  220. controller: _textController,
  221. focusNode: _focusNode,
  222. ),
  223. ),
  224. ),
  225. IconButton(
  226. icon: const Icon(Icons.search),
  227. onPressed: () {
  228. _showSearch = false;
  229. _keyword = _textController.text;
  230. setState(() {});
  231. },
  232. ),
  233. ],
  234. ),
  235. ),
  236. );
  237. }
  238. }